#include "config.h"
#if ENABLE(WEB_RTC)
#include "SDPProcessor.h"
#include "Document.h"
#include "Frame.h"
#include "SDPProcessorScriptResource.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include "inspector/InspectorValues.h"
#include <bindings/ScriptObject.h>
#include <wtf/NeverDestroyed.h>
using namespace Inspector;
namespace WebCore {
#define STRING_FUNCTION(name) \
static const String& name##String() \
{ \
static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
return name; \
}
STRING_FUNCTION(address)
STRING_FUNCTION(apt)
STRING_FUNCTION(candidates)
STRING_FUNCTION(ccmfir)
STRING_FUNCTION(channels)
STRING_FUNCTION(clockRate)
STRING_FUNCTION(cname)
STRING_FUNCTION(componentId)
STRING_FUNCTION(dtls)
STRING_FUNCTION(encodingName)
STRING_FUNCTION(fingerprint)
STRING_FUNCTION(fingerprintHashFunction)
STRING_FUNCTION(foundation)
STRING_FUNCTION(ice)
STRING_FUNCTION(mediaDescriptions)
STRING_FUNCTION(mediaStreamId)
STRING_FUNCTION(mediaStreamTrackId)
STRING_FUNCTION(mid)
STRING_FUNCTION(mode)
STRING_FUNCTION(mux)
STRING_FUNCTION(nack)
STRING_FUNCTION(nackpli)
STRING_FUNCTION(originator)
STRING_FUNCTION(packetizationMode)
STRING_FUNCTION(parameters)
STRING_FUNCTION(password)
STRING_FUNCTION(payloads)
STRING_FUNCTION(port)
STRING_FUNCTION(priority)
STRING_FUNCTION(relatedAddress)
STRING_FUNCTION(relatedPort)
STRING_FUNCTION(rtcp)
STRING_FUNCTION(rtcpAddress)
STRING_FUNCTION(rtcpPort)
STRING_FUNCTION(rtxTime)
STRING_FUNCTION(sessionId)
STRING_FUNCTION(sessionVersion)
STRING_FUNCTION(setup)
STRING_FUNCTION(ssrcs)
STRING_FUNCTION(tcpType)
STRING_FUNCTION(transport)
STRING_FUNCTION(type)
STRING_FUNCTION(ufrag)
SDPProcessor::SDPProcessor(ScriptExecutionContext* context)
: ContextDestructionObserver(context)
{
}
static RefPtr<InspectorObject> createCandidateObject(const IceCandidate& candidate)
{
RefPtr<InspectorObject> candidateObject = InspectorObject::create();
candidateObject->setString(typeString(), candidate.type());
candidateObject->setString(foundationString(), candidate.foundation());
candidateObject->setInteger(componentIdString(), candidate.componentId());
candidateObject->setString(transportString(), candidate.transport());
candidateObject->setInteger(priorityString(), candidate.priority());
candidateObject->setString(addressString(), candidate.address());
candidateObject->setInteger(portString(), candidate.port());
if (!candidate.tcpType().isEmpty())
candidateObject->setString(tcpTypeString(), candidate.tcpType());
if (candidate.type().convertToASCIIUppercase() != "HOST") {
candidateObject->setString(relatedAddressString(), candidate.relatedAddress());
candidateObject->setInteger(relatedPortString(), candidate.relatedPort());
}
return candidateObject;
}
static RefPtr<IceCandidate> createCandidate(const InspectorObject& candidateObject)
{
RefPtr<IceCandidate> candidate = IceCandidate::create();
String stringValue;
unsigned intValue;
if (candidateObject.getString(typeString(), stringValue))
candidate->setType(stringValue);
if (candidateObject.getString(foundationString(), stringValue))
candidate->setFoundation(stringValue);
if (candidateObject.getInteger(componentIdString(), intValue))
candidate->setComponentId(intValue);
if (candidateObject.getString(transportString(), stringValue))
candidate->setTransport(stringValue);
if (candidateObject.getInteger(priorityString(), intValue))
candidate->setPriority(intValue);
if (candidateObject.getString(addressString(), stringValue))
candidate->setAddress(stringValue);
if (candidateObject.getInteger(portString(), intValue))
candidate->setPort(intValue);
if (candidateObject.getString(tcpTypeString(), stringValue))
candidate->setTcpType(stringValue);
if (candidateObject.getString(relatedAddressString(), stringValue))
candidate->setRelatedAddress(stringValue);
if (candidateObject.getInteger(relatedPortString(), intValue))
candidate->setRelatedPort(intValue);
return candidate;
}
static RefPtr<MediaEndpointSessionConfiguration> configurationFromJSON(const String& json)
{
RefPtr<InspectorValue> value;
if (!InspectorValue::parseJSON(json, value))
return nullptr;
RefPtr<InspectorObject> object;
if (!value->asObject(object))
return nullptr;
RefPtr<MediaEndpointSessionConfiguration> configuration = MediaEndpointSessionConfiguration::create();
String stringValue;
unsigned intValue;
unsigned long longValue;
bool boolValue;
RefPtr<InspectorObject> originatorObject = InspectorObject::create();
if (object->getObject(originatorString(), originatorObject)) {
if (originatorObject->getInteger(sessionIdString(), longValue))
configuration->setSessionId(longValue);
if (originatorObject->getInteger(sessionVersionString(), intValue))
configuration->setSessionVersion(intValue);
}
RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
object->getArray(mediaDescriptionsString(), mediaDescriptionsArray);
for (unsigned i = 0; i < mediaDescriptionsArray->length(); ++i) {
RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
mediaDescriptionsArray->get(i)->asObject(mediaDescriptionObject);
RefPtr<PeerMediaDescription> mediaDescription = PeerMediaDescription::create();
if (mediaDescriptionObject->getString(typeString(), stringValue))
mediaDescription->setType(stringValue);
if (mediaDescriptionObject->getInteger(portString(), intValue))
mediaDescription->setPort(intValue);
if (mediaDescriptionObject->getString(addressString(), stringValue))
mediaDescription->setAddress(stringValue);
if (mediaDescriptionObject->getString(modeString(), stringValue))
mediaDescription->setMode(stringValue);
if (mediaDescriptionObject->getString(midString(), stringValue))
mediaDescription->setMid(stringValue);
RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
mediaDescriptionObject->getArray(payloadsString(), payloadsArray);
for (unsigned j = 0; j < payloadsArray->length(); ++j) {
RefPtr<InspectorObject> payloadsObject = InspectorObject::create();
payloadsArray->get(j)->asObject(payloadsObject);
RefPtr<MediaPayload> payload = MediaPayload::create();
if (payloadsObject->getInteger(typeString(), intValue))
payload->setType(intValue);
if (payloadsObject->getString(encodingNameString(), stringValue))
payload->setEncodingName(stringValue);
if (payloadsObject->getInteger(clockRateString(), intValue))
payload->setClockRate(intValue);
if (payloadsObject->getInteger(channelsString(), intValue))
payload->setChannels(intValue);
if (payloadsObject->getBoolean(ccmfirString(), boolValue))
payload->setCcmfir(boolValue);
if (payloadsObject->getBoolean(nackpliString(), boolValue))
payload->setNackpli(boolValue);
if (payloadsObject->getBoolean(nackString(), boolValue))
payload->setNack(boolValue);
RefPtr<InspectorObject> parametersObject = InspectorObject::create();
if (payloadsObject->getObject(parametersString(), parametersObject)) {
if (parametersObject->getInteger(packetizationModeString(), intValue))
payload->addParameter("packetizationMode", intValue);
if (parametersObject->getInteger(aptString(), intValue))
payload->addParameter("apt", intValue);
if (parametersObject->getInteger(rtxTimeString(), intValue))
payload->addParameter("rtxTime", intValue);
}
mediaDescription->addPayload(WTFMove(payload));
}
RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
if (mediaDescriptionObject->getObject(rtcpString(), rtcpObject)) {
if (rtcpObject->getBoolean(muxString(), boolValue))
mediaDescription->setRtcpMux(boolValue);
if (rtcpObject->getString(rtcpAddressString(), stringValue))
mediaDescription->setRtcpAddress(stringValue);
if (rtcpObject->getInteger(rtcpPortString(), intValue))
mediaDescription->setRtcpPort(intValue);
}
if (mediaDescriptionObject->getString(mediaStreamIdString(), stringValue))
mediaDescription->setMediaStreamId(stringValue);
if (mediaDescriptionObject->getString(mediaStreamTrackIdString(), stringValue))
mediaDescription->setMediaStreamTrackId(stringValue);
RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
if (mediaDescriptionObject->getObject(dtlsString(), dtlsObject)) {
if (dtlsObject->getString(setupString(), stringValue))
mediaDescription->setDtlsSetup(stringValue);
if (dtlsObject->getString(fingerprintHashFunctionString(), stringValue))
mediaDescription->setDtlsFingerprintHashFunction(stringValue);
if (dtlsObject->getString(fingerprintString(), stringValue))
mediaDescription->setDtlsFingerprint(stringValue);
}
RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
mediaDescriptionObject->getArray(ssrcsString(), ssrcsArray);
for (unsigned j = 0; j < ssrcsArray->length(); ++j) {
ssrcsArray->get(j)->asInteger(intValue);
mediaDescription->addSsrc(intValue);
}
if (mediaDescriptionObject->getString(cnameString(), stringValue))
mediaDescription->setCname(stringValue);
RefPtr<InspectorObject> iceObject = InspectorObject::create();
if (mediaDescriptionObject->getObject(iceString(), iceObject)) {
if (iceObject->getString(ufragString(), stringValue))
mediaDescription->setIceUfrag(stringValue);
if (iceObject->getString(passwordString(), stringValue))
mediaDescription->setIcePassword(stringValue);
RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
iceObject->getArray(candidatesString(), candidatesArray);
for (unsigned j = 0; j < candidatesArray->length(); ++j) {
RefPtr<InspectorObject> candidateObject = InspectorObject::create();
candidatesArray->get(j)->asObject(candidateObject);
mediaDescription->addIceCandidate(createCandidate(*candidateObject));
}
}
configuration->addMediaDescription(WTFMove(mediaDescription));
}
return configuration;
}
static RefPtr<IceCandidate> iceCandidateFromJSON(const String& json)
{
RefPtr<InspectorValue> value;
if (!InspectorValue::parseJSON(json, value))
return nullptr;
RefPtr<InspectorObject> candidateObject;
if (!value->asObject(candidateObject))
return nullptr;
return createCandidate(*candidateObject);
}
static String configurationToJSON(const MediaEndpointSessionConfiguration& configuration)
{
RefPtr<InspectorObject> object = InspectorObject::create();
RefPtr<InspectorObject> originatorObject = InspectorObject::create();
originatorObject->setDouble(sessionIdString(), configuration.sessionId());
originatorObject->setInteger(sessionVersionString(), configuration.sessionVersion());
object->setObject(originatorString(), originatorObject);
RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
for (const RefPtr<PeerMediaDescription>& mediaDescription : configuration.mediaDescriptions()) {
RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
mediaDescriptionObject->setString(typeString(), mediaDescription->type());
mediaDescriptionObject->setInteger(portString(), mediaDescription->port());
mediaDescriptionObject->setString(addressString(), mediaDescription->address());
mediaDescriptionObject->setString(modeString(), mediaDescription->mode());
mediaDescriptionObject->setString(midString(), mediaDescription->mid());
RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
for (RefPtr<MediaPayload> payload : mediaDescription->payloads()) {
RefPtr<InspectorObject> payloadObject = InspectorObject::create();
payloadObject->setInteger(typeString(), payload->type());
payloadObject->setString(encodingNameString(), payload->encodingName());
payloadObject->setInteger(clockRateString(), payload->clockRate());
payloadObject->setInteger(channelsString(), payload->channels());
payloadObject->setBoolean(ccmfirString(), payload->ccmfir());
payloadObject->setBoolean(nackpliString(), payload->nackpli());
payloadObject->setBoolean(nackString(), payload->nack());
if (!payload->parameters().isEmpty()) {
RefPtr<InspectorObject> parametersObject = InspectorObject::create();
for (auto& name : payload->parameters().keys())
parametersObject->setInteger(name, payload->parameters().get(name));
payloadObject->setObject(parametersString(), parametersObject);
}
payloadsArray->pushObject(payloadObject);
}
mediaDescriptionObject->setArray(payloadsString(), payloadsArray);
RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
rtcpObject->setBoolean(muxString(), mediaDescription->rtcpMux());
rtcpObject->setString(addressString(), mediaDescription->rtcpAddress());
rtcpObject->setInteger(portString(), mediaDescription->rtcpPort());
mediaDescriptionObject->setObject(rtcpString(), rtcpObject);
mediaDescriptionObject->setString(mediaStreamIdString(), mediaDescription->mediaStreamId());
mediaDescriptionObject->setString(mediaStreamTrackIdString(), mediaDescription->mediaStreamTrackId());
RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
dtlsObject->setString(setupString(), mediaDescription->dtlsSetup());
dtlsObject->setString(fingerprintHashFunctionString(), mediaDescription->dtlsFingerprintHashFunction());
dtlsObject->setString(fingerprintString(), mediaDescription->dtlsFingerprint());
mediaDescriptionObject->setObject(dtlsString(), dtlsObject);
RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
for (auto ssrc : mediaDescription->ssrcs())
ssrcsArray->pushDouble(ssrc);
mediaDescriptionObject->setArray(ssrcsString(), ssrcsArray);
mediaDescriptionObject->setString(cnameString(), mediaDescription->cname());
RefPtr<InspectorObject> iceObject = InspectorObject::create();
iceObject->setString(ufragString(), mediaDescription->iceUfrag());
iceObject->setString(passwordString(), mediaDescription->icePassword());
RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
for (RefPtr<IceCandidate> candidate : mediaDescription->iceCandidates())
candidatesArray->pushObject(createCandidateObject(*candidate));
iceObject->setArray(candidatesString(), candidatesArray);
mediaDescriptionObject->setObject(iceString(), iceObject);
mediaDescriptionsArray->pushObject(mediaDescriptionObject);
}
object->setArray(mediaDescriptionsString(), mediaDescriptionsArray);
return object->toJSONString();
}
static String iceCandidateToJSON(const IceCandidate& candidate)
{
return createCandidateObject(candidate)->toJSONString();
}
SDPProcessor::Result SDPProcessor::generate(const MediaEndpointSessionConfiguration& configuration, String& outSdpString) const
{
String sdpString;
if (!callScript("generate", configurationToJSON(configuration), sdpString))
return Result::InternalError;
outSdpString = sdpString;
return Result::Success;
}
SDPProcessor::Result SDPProcessor::parse(const String& sdp, RefPtr<MediaEndpointSessionConfiguration>& outConfiguration) const
{
String scriptOutput;
if (!callScript("parse", sdp, scriptOutput))
return Result::InternalError;
if (scriptOutput == "ParseError")
return Result::ParseError;
RefPtr<MediaEndpointSessionConfiguration> configuration = configurationFromJSON(scriptOutput);
if (!configuration)
return Result::InternalError;
outConfiguration = configuration;
return Result::Success;
}
SDPProcessor::Result SDPProcessor::generateCandidateLine(const IceCandidate& candidate, String& outCandidateLine) const
{
String candidateLine;
if (!callScript("generateCandidateLine", iceCandidateToJSON(candidate), candidateLine))
return Result::InternalError;
outCandidateLine = candidateLine;
return Result::Success;
}
SDPProcessor::Result SDPProcessor::parseCandidateLine(const String& candidateLine, RefPtr<IceCandidate>& outCandidate) const
{
String scriptOutput;
if (!callScript("parseCandidateLine", candidateLine, scriptOutput))
return Result::InternalError;
if (scriptOutput == "ParseError")
return Result::ParseError;
RefPtr<IceCandidate> candidate = iceCandidateFromJSON(scriptOutput);
if (!candidate)
return Result::InternalError;
outCandidate = candidate;
return Result::Success;
}
bool SDPProcessor::callScript(const String& functionName, const String& argument, String& outResult) const
{
if (!scriptExecutionContext())
return false;
Document* document = downcast<Document>(scriptExecutionContext());
if (!document->frame())
return false;
if (!m_isolatedWorld)
m_isolatedWorld = DOMWrapperWorld::create(JSDOMWindow::commonVM());
ScriptController& scriptController = document->frame()->script();
JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(*m_isolatedWorld));
JSC::ExecState* exec = globalObject->globalExec();
JSC::JSLockHolder lock(exec);
JSC::JSValue probeFunctionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, "generate"));
if (!probeFunctionValue.isFunction()) {
URL scriptURL;
scriptController.evaluateInWorld(ScriptSourceCode(SDPProcessorScriptResource::scriptString(), scriptURL), *m_isolatedWorld);
if (exec->hadException()) {
exec->clearException();
return false;
}
}
JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, functionName));
if (!functionValue.isFunction())
return false;
JSC::JSObject* function = functionValue.toObject(exec);
JSC::CallData callData;
JSC::CallType callType = function->methodTable()->getCallData(function, callData);
if (callType == JSC::CallType::None)
return false;
JSC::MarkedArgumentBuffer argList;
argList.append(JSC::jsString(exec, argument));
JSC::JSValue result = JSC::call(exec, function, callType, callData, globalObject, argList);
if (exec->hadException()) {
LOG_ERROR("SDPProcessor script threw in function %s", functionName.ascii().data());
exec->clearException();
return false;
}
if (!result.isString())
return false;
outResult = result.getString(exec);
return true;
}
}
#endif // ENABLE(WEB_RTC)