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); }
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())); }