static void JSMediaDevicesGetUserMediaPromiseFunction(ExecState& state, Ref<DeferredPromise>&& promise)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (UNLIKELY(state.argumentCount() < 1)) {
        throwVMError(&state, scope, createNotEnoughArgumentsError(&state));
        return;
    }

    auto constraintsDictionary = Dictionary(&state, state.uncheckedArgument(0));

    MediaTrackConstraintSetMap mandatoryAudioConstraints;
    Vector<MediaTrackConstraintSetMap> advancedAudioConstraints;
    bool areAudioConstraintsValid = false;

    Dictionary audioConstraintsDictionary;
    if (constraintsDictionary.get("audio", audioConstraintsDictionary) && !audioConstraintsDictionary.isUndefinedOrNull()) {
        parseMediaConstraintsDictionary(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints);
        areAudioConstraintsValid = true;
    } else
        constraintsDictionary.get("audio", areAudioConstraintsValid);

    MediaTrackConstraintSetMap mandatoryVideoConstraints;
    Vector<MediaTrackConstraintSetMap> advancedVideoConstraints;
    bool areVideoConstraintsValid = false;

    Dictionary videoConstraintsDictionary;
    if (constraintsDictionary.get("video", videoConstraintsDictionary) && !videoConstraintsDictionary.isUndefinedOrNull()) {
        parseMediaConstraintsDictionary(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints);
        areVideoConstraintsValid = true;
    } else
        constraintsDictionary.get("video", areVideoConstraintsValid);

    auto audioConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryAudioConstraints), WTFMove(advancedAudioConstraints), areAudioConstraintsValid);
    auto videoConstraints = MediaConstraintsImpl::create(WTFMove(mandatoryVideoConstraints), WTFMove(advancedVideoConstraints), areVideoConstraintsValid);
    propagateException(state, scope, castThisValue<JSMediaDevices>(state).wrapped().getUserMedia(WTFMove(audioConstraints), WTFMove(videoConstraints), WTFMove(promise)));
}
static JSValue getObjectParameter(JSWebGLRenderingContextBase* obj, ExecState& state, ObjectType objectType)
{
    if (state.argumentCount() != 2)
        return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));
    
    ExceptionCode ec = 0;
    WebGLRenderingContextBase& context = obj->wrapped();
    unsigned target = state.uncheckedArgument(0).toInt32(&state);
    if (state.hadException())
        return jsUndefined();
    unsigned pname = state.uncheckedArgument(1).toInt32(&state);
    if (state.hadException())
        return jsUndefined();
    WebGLGetInfo info;
    switch (objectType) {
    case kBuffer:
        info = context.getBufferParameter(target, pname, ec);
        break;
    case kRenderbuffer:
        info = context.getRenderbufferParameter(target, pname, ec);
        break;
    case kTexture:
        info = context.getTexParameter(target, pname, ec);
        break;
    case kVertexAttrib:
        // target => index
        info = context.getVertexAttrib(target, pname, ec);
        break;
    default:
        notImplemented();
        break;
    }
    if (ec) {
        setDOMException(&state, ec);
        return jsUndefined();
    }
    return toJS(&state, obj->globalObject(), info);
}
Beispiel #3
0
JSValue JSNode::replaceChild(ExecState& state)
{
    if (UNLIKELY(state.argumentCount() < 2))
        return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));

    auto* newChild = JSNode::toWrapped(state.uncheckedArgument(0));
    JSValue oldChildValue = state.uncheckedArgument(1);
    auto* oldChild = JSNode::toWrapped(oldChildValue);
    if (UNLIKELY(!newChild || !oldChild)) {
        if (!newChild)
            return JSValue::decode(throwArgumentTypeError(state, 0, "node", "Node", "replaceChild", "Node"));
        return JSValue::decode(throwArgumentTypeError(state, 1, "child", "Node", "replaceChild", "Node"));
    }

    ExceptionCode ec = 0;
    if (UNLIKELY(!wrapped().replaceChild(*newChild, *oldChild, ec))) {
        setDOMException(&state, ec);
        return jsUndefined();
    }

    ASSERT(!ec);
    return oldChildValue;
}
JSValue JSWebGLRenderingContextBase::getFramebufferAttachmentParameter(ExecState& state)
{
    if (state.argumentCount() != 3)
        return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));
    
    ExceptionCode ec = 0;
    WebGLRenderingContextBase& context = wrapped();
    unsigned target = state.uncheckedArgument(0).toInt32(&state);
    if (state.hadException())
        return jsUndefined();
    unsigned attachment = state.uncheckedArgument(1).toInt32(&state);
    if (state.hadException())
        return jsUndefined();
    unsigned pname = state.uncheckedArgument(2).toInt32(&state);
    if (state.hadException())
        return jsUndefined();
    WebGLGetInfo info = context.getFramebufferAttachmentParameter(target, attachment, pname, ec);
    if (ec) {
        setDOMException(&state, ec);
        return jsUndefined();
    }
    return toJS(&state, globalObject(), info);
}
JSValue JSWebKitSubtleCrypto::unwrapKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 5)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    auto keyFormat = cryptoKeyFormatFromJSValue(state, scope, state.uncheckedArgument(0));
    RETURN_IF_EXCEPTION(scope, { });

    auto wrappedKeyData = cryptoOperationDataFromJSValue(state, scope, state.uncheckedArgument(1));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<CryptoKey> unwrappingKey = JSCryptoKey::toWrapped(vm, state.uncheckedArgument(2));
    if (!unwrappingKey)
        return throwTypeError(&state, scope);

    if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) {
        wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'unwrapKey'"));
        throwNotSupportedError(state, scope);
        return jsUndefined();
    }

    auto unwrapAlgorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(3));
    RETURN_IF_EXCEPTION(scope, { });

    auto unwrapAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(state, scope, unwrapAlgorithm->identifier(), state.uncheckedArgument(3));
    RETURN_IF_EXCEPTION(scope, { });

    RefPtr<CryptoAlgorithm> unwrappedKeyAlgorithm;
    RefPtr<CryptoAlgorithmParametersDeprecated> unwrappedKeyAlgorithmParameters;
    if (!state.uncheckedArgument(4).isNull()) {
        unwrappedKeyAlgorithm = createAlgorithmFromJSValue(state, scope, state.uncheckedArgument(4));
        RETURN_IF_EXCEPTION(scope, { });

        unwrappedKeyAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(state, scope, unwrappedKeyAlgorithm->identifier(), state.uncheckedArgument(4));
        RETURN_IF_EXCEPTION(scope, { });
    }

    bool extractable = state.argument(5).toBoolean(&state);
    RETURN_IF_EXCEPTION(scope, { });

    CryptoKeyUsageBitmap keyUsages = 0;
    if (state.argumentCount() >= 7) {
        keyUsages = cryptoKeyUsagesFromJSValue(state, scope, state.uncheckedArgument(6));
        RETURN_IF_EXCEPTION(scope, { });
    }

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    Strong<JSDOMGlobalObject> domGlobalObject(state.vm(), globalObject());

    auto decryptSuccessCallback = [domGlobalObject, keyFormat, unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, wrapper](const Vector<uint8_t>& result) mutable {
        auto importSuccessCallback = [wrapper](CryptoKey& key) mutable {
            wrapper->resolve<IDLInterface<CryptoKey>>(key);
        };
        auto importFailureCallback = [wrapper]() mutable {
            wrapper->reject(); // FIXME: This should reject with an Exception.
        };

        VM& vm = domGlobalObject->vm();
        auto scope = DECLARE_CATCH_SCOPE(vm);

        ExecState& state = *domGlobalObject->globalExec();
        WebCore::importKey(state, keyFormat, std::make_pair(result.data(), result.size()), unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, WTFMove(importSuccessCallback), WTFMove(importFailureCallback));
        if (UNLIKELY(scope.exception())) {
            // FIXME: Report exception details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
            scope.clearException();
            wrapper->reject(); // FIXME: This should reject with an Exception.
        }
    };

    auto decryptFailureCallback = [wrapper]() mutable {
        wrapper->reject(); // FIXME: This should reject with an Exception.
    };

    auto result = unwrapAlgorithm->decryptForUnwrapKey(*unwrapAlgorithmParameters, *unwrappingKey, wrappedKeyData, WTFMove(decryptSuccessCallback), WTFMove(decryptFailureCallback));
    if (result.hasException()) {
        propagateException(state, scope, result.releaseException());
        return { };
    }

    return promise;
}
JSValue JSWebKitSubtleCrypto::wrapKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 4)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    CryptoKeyFormat keyFormat;
    auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
    ASSERT(scope.exception() || success);
    if (!success)
        return jsUndefined();

    RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
    if (!key)
        return throwTypeError(&state, scope);

    RefPtr<CryptoKey> wrappingKey = JSCryptoKey::toWrapped(state.uncheckedArgument(2));
    if (!key)
        return throwTypeError(&state, scope);

    if (!wrappingKey->allows(CryptoKeyUsageWrapKey)) {
        wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'wrapKey'"));
        setDOMException(&state, NOT_SUPPORTED_ERR);
        return jsUndefined();
    }

    auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(3));
    ASSERT(scope.exception() || algorithm);
    if (!algorithm)
        return jsUndefined();

    auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(&state, algorithm->identifier(), state.uncheckedArgument(3));
    ASSERT(scope.exception() || parameters);
    if (!parameters)
        return jsUndefined();

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();

    auto exportSuccessCallback = [keyFormat, algorithm, parameters, wrappingKey, wrapper](const Vector<uint8_t>& exportedKeyData) mutable {
        auto encryptSuccessCallback = [wrapper](const Vector<uint8_t>& encryptedData) mutable {
            fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), encryptedData.data(), encryptedData.size());
        };
        auto encryptFailureCallback = [wrapper]() mutable {
            wrapper->reject(nullptr);
        };
        auto result = algorithm->encryptForWrapKey(*parameters, *wrappingKey, std::make_pair(exportedKeyData.data(), exportedKeyData.size()), WTFMove(encryptSuccessCallback), WTFMove(encryptFailureCallback));
        if (result.hasException()) {
            // FIXME: Report failure details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
            wrapper->reject(nullptr);
        }
    };

    auto exportFailureCallback = [wrapper]() mutable {
        wrapper->reject(nullptr);
    };

    WebCore::exportKey(state, keyFormat, *key, WTFMove(exportSuccessCallback), WTFMove(exportFailureCallback));

    return promise;
}
JSValue JSWebKitSubtleCrypto::importKey(ExecState& state)
{
    VM& vm = state.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    if (state.argumentCount() < 3)
        return throwException(&state, scope, createNotEnoughArgumentsError(&state));

    CryptoKeyFormat keyFormat;
    auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
    ASSERT(scope.exception() || success);
    if (!success)
        return jsUndefined();

    CryptoOperationData data;
    success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(1), data);
    ASSERT(scope.exception() || success);
    if (!success)
        return jsUndefined();

    RefPtr<CryptoAlgorithm> algorithm;
    RefPtr<CryptoAlgorithmParametersDeprecated> parameters;
    if (!state.uncheckedArgument(2).isNull()) {
        algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(2));
        ASSERT(scope.exception() || algorithm);
        if (!algorithm)
            return jsUndefined();

        parameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(&state, algorithm->identifier(), state.uncheckedArgument(2));
        ASSERT(scope.exception() || parameters);
        if (!parameters)
            return jsUndefined();
    }

    bool extractable = false;
    if (state.argumentCount() >= 4) {
        extractable = state.uncheckedArgument(3).toBoolean(&state);
        RETURN_IF_EXCEPTION(scope, JSValue());
    }

    CryptoKeyUsageBitmap keyUsages = 0;
    if (state.argumentCount() >= 5) {
        auto success = cryptoKeyUsagesFromJSValue(state, state.argument(4), keyUsages);
        ASSERT(scope.exception() || success);
        if (!success)
            return jsUndefined();
    }

    RefPtr<DeferredPromise> wrapper = createDeferredPromise(state, domWindow());
    auto promise = wrapper->promise();
    auto successCallback = [wrapper](CryptoKey& result) mutable {
        wrapper->resolve(result);
    };
    auto failureCallback = [wrapper]() mutable {
        wrapper->reject(nullptr);
    };

    WebCore::importKey(state, keyFormat, data, WTFMove(algorithm), WTFMove(parameters), extractable, keyUsages, WTFMove(successCallback), WTFMove(failureCallback));
    RETURN_IF_EXCEPTION(scope, JSValue());

    return promise;
}
// https://html.spec.whatwg.org/#dom-customelementsregistry-define
JSValue JSCustomElementsRegistry::define(ExecState& state)
{
    if (UNLIKELY(state.argumentCount() < 2))
        return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));

    AtomicString localName(state.uncheckedArgument(0).toString(&state)->toAtomicString(&state));
    if (UNLIKELY(state.hadException()))
        return jsUndefined();

    JSValue constructorValue = state.uncheckedArgument(1);
    if (!constructorValue.isConstructor())
        return throwTypeError(&state, ASCIILiteral("The second argument must be a constructor"));
    JSObject* constructor = constructorValue.getObject();

    // FIXME: Throw a TypeError if constructor doesn't inherit from HTMLElement.
    // https://github.com/w3c/webcomponents/issues/541

    switch (Document::validateCustomElementName(localName)) {
    case CustomElementNameValidationStatus::Valid:
        break;
    case CustomElementNameValidationStatus::ConflictsWithBuiltinNames:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name cannot be same as one of the builtin elements"));
    case CustomElementNameValidationStatus::NoHyphen:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name must contain a hyphen"));
    case CustomElementNameValidationStatus::ContainsUpperCase:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name cannot contain an upper case letter"));
    }

    // FIXME: Check re-entrancy here.
    // https://github.com/w3c/webcomponents/issues/545

    CustomElementsRegistry& registry = wrapped();
    if (registry.findInterface(localName)) {
        throwNotSupportedError(state, ASCIILiteral("Cannot define multiple custom elements with the same tag name"));
        return jsUndefined();
    }

    if (registry.containsConstructor(constructor)) {
        throwNotSupportedError(state, ASCIILiteral("Cannot define multiple custom elements with the same class"));
        return jsUndefined();
    }

    auto& vm = globalObject()->vm();
    JSValue prototypeValue = constructor->get(&state, vm.propertyNames->prototype);
    if (state.hadException())
        return jsUndefined();
    if (!prototypeValue.isObject())
        return throwTypeError(&state, ASCIILiteral("Custom element constructor's prototype must be an object"));
    JSObject& prototypeObject = *asObject(prototypeValue);

    QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI);
    auto elementInterface = JSCustomElementInterface::create(name, constructor, globalObject());

    auto* connectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (connectedCallback)
        elementInterface->setConnectedCallback(connectedCallback);

    auto* disconnectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (disconnectedCallback)
        elementInterface->setDisconnectedCallback(disconnectedCallback);

    // FIXME: Add the support for adoptedCallback.
    getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "adoptedCallback"));
    if (state.hadException())
        return jsUndefined();

    auto* attributeChangedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "attributeChangedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (attributeChangedCallback) {
        auto value = convertOptional<Vector<String>>(state, constructor->get(&state, Identifier::fromString(&state, "observedAttributes")));
        if (state.hadException())
            return jsUndefined();
        if (value)
            elementInterface->setAttributeChangedCallback(attributeChangedCallback, *value);
    }

    PrivateName uniquePrivateName;
    globalObject()->putDirect(vm, uniquePrivateName, constructor);

    registry.addElementDefinition(WTFMove(elementInterface));

    // FIXME: 17. Let map be registry's upgrade candidates map.
    // FIXME: 18. Upgrade a newly-defined element given map and definition.
    // FIXME: 19. Resolve whenDefined promise.

    return jsUndefined();
}
EncodedJSValue JSC_HOST_CALL constructJSAudioContext(ExecState& exec)
{
    DOMConstructorObject* jsConstructor = jsCast<DOMConstructorObject*>(exec.callee());
    if (!jsConstructor)
        return throwVMError(&exec, createReferenceError(&exec, "AudioContext constructor callee is unavailable"));

    ScriptExecutionContext* scriptExecutionContext = jsConstructor->scriptExecutionContext();
    if (!scriptExecutionContext)
        return throwVMError(&exec, createReferenceError(&exec, "AudioContext constructor script execution context is unavailable"));

    if (!is<Document>(*scriptExecutionContext))
        return throwVMError(&exec, createReferenceError(&exec, "AudioContext constructor called in a script execution context which is not a document"));

    Document& document = downcast<Document>(*scriptExecutionContext);

    RefPtr<AudioContext> audioContext;

    if (!exec.argumentCount()) {
        // Constructor for default AudioContext which talks to audio hardware.
        ExceptionCode ec = 0;
        audioContext = AudioContext::create(document, ec);
        if (ec) {
            setDOMException(&exec, ec);
            return JSValue::encode(JSValue());
        }
        if (!audioContext.get())
            return throwVMError(&exec, createSyntaxError(&exec, "audio resources unavailable for AudioContext construction"));
    } else {
#if ENABLE(LEGACY_WEB_AUDIO)
        // Constructor for offline (render-target) AudioContext which renders into an AudioBuffer.
        // new AudioContext(in unsigned long numberOfChannels, in unsigned long numberOfFrames, in float sampleRate);
        document.addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Deprecated AudioContext constructor: use OfflineAudioContext instead"));

        if (exec.argumentCount() < 3)
            return throwVMError(&exec, createNotEnoughArgumentsError(&exec));

        int32_t numberOfChannels = exec.uncheckedArgument(0).toInt32(&exec);
        int32_t numberOfFrames = exec.uncheckedArgument(1).toInt32(&exec);
        float sampleRate = exec.uncheckedArgument(2).toFloat(&exec);

        if (numberOfChannels <= 0 || numberOfChannels > 10)
            return throwVMError(&exec, createSyntaxError(&exec, "Invalid number of channels"));

        if (numberOfFrames <= 0)
            return throwVMError(&exec, createSyntaxError(&exec, "Invalid number of frames"));

        if (sampleRate <= 0)
            return throwVMError(&exec, createSyntaxError(&exec, "Invalid sample rate"));


        ExceptionCode ec = 0;
        audioContext = OfflineAudioContext::create(document, numberOfChannels, numberOfFrames, sampleRate, ec);
        if (ec) {
            setDOMException(&exec, ec);
            return throwVMError(&exec, createSyntaxError(&exec, "Error creating OfflineAudioContext"));
        }
#else
        return throwVMError(&exec, createSyntaxError(&exec, "Illegal AudioContext constructor"));
#endif
    }

    if (!audioContext)
        return throwVMError(&exec, createReferenceError(&exec, "Error creating AudioContext"));

    return JSValue::encode(CREATE_DOM_WRAPPER(jsConstructor->globalObject(), AudioContext, audioContext.releaseNonNull()));
}