bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception) { if (!ctx) { ASSERT_NOT_REACHED(); return false; } ExecState* exec = toJS(ctx); JSLockHolder locker(exec); JSValue jsA = toJS(exec, a); JSValue jsB = toJS(exec, b); bool result = JSValue::equal(exec, jsA, jsB); // false if an exception is thrown if (exec->hadException()) { JSValue exceptionValue = exec->exception(); if (exception) *exception = toRef(exec, exceptionValue); exec->clearException(); #if ENABLE(REMOTE_INSPECTOR) exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); #endif } return result; }
EXPORT JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { JSLock lock; ExecState* exec = toJS(ctx); JSObject* jsObject = toJS(object); JSObject* jsThisObject = toJS(thisObject); if (!jsThisObject) jsThisObject = exec->dynamicInterpreter()->globalObject(); List argList; for (size_t i = 0; i < argumentCount; i++) argList.append(toJS(arguments[i])); JSValueRef result = toRef(jsObject->call(exec, jsThisObject, argList)); // returns NULL if object->implementsCall() is false if (exec->hadException()) { if (exception) *exception = toRef(exec->exception()); exec->clearException(); result = 0; } return result; }
JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned parameterCount, const JSStringRef parameterNames[], JSStringRef body, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception) { JSLock lock; ExecState* exec = toJS(ctx); UString::Rep* bodyRep = toJS(body); UString::Rep* sourceURLRep = sourceURL ? toJS(sourceURL) : &UString::Rep::null; Identifier nameID = name ? Identifier(toJS(name)) : Identifier("anonymous"); List args; for (unsigned i = 0; i < parameterCount; i++) args.append(jsString(UString(toJS(parameterNames[i])))); args.append(jsString(UString(bodyRep))); JSObject* result = exec->dynamicInterpreter()->builtinFunction()->construct(exec, args, nameID, UString(sourceURLRep), startingLineNumber); if (exec->hadException()) { if (exception) *exception = toRef(exec->exception()); exec->clearException(); result = 0; } return toRef(result); }
JSValue JSSubtleCrypto::unwrapKey(ExecState* exec) { if (exec->argumentCount() < 5) return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec)); CryptoKeyFormat keyFormat; if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) { ASSERT(exec->hadException()); return jsUndefined(); } CryptoOperationData wrappedKeyData; if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), wrappedKeyData)) { ASSERT(exec->hadException()); return jsUndefined(); } RefPtr<CryptoKey> unwrappingKey = JSCryptoKey::toWrapped(exec->uncheckedArgument(2)); if (!unwrappingKey) return throwTypeError(exec); if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) { m_impl->document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'unwrapKey'")); setDOMException(exec, NOT_SUPPORTED_ERR); return jsUndefined(); } std::unique_ptr<CryptoAlgorithm> unwrapAlgorithm; std::unique_ptr<CryptoAlgorithmParameters> unwrapAlgorithmParameters; unwrapAlgorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(3)); if (!unwrapAlgorithm) { ASSERT(exec->hadException()); return jsUndefined(); } unwrapAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(exec, unwrapAlgorithm->identifier(), exec->uncheckedArgument(3)); if (!unwrapAlgorithmParameters) { ASSERT(exec->hadException()); return jsUndefined(); } std::unique_ptr<CryptoAlgorithm> unwrappedKeyAlgorithm; std::unique_ptr<CryptoAlgorithmParameters> unwrappedKeyAlgorithmParameters; if (!exec->uncheckedArgument(4).isNull()) { unwrappedKeyAlgorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(4)); if (!unwrappedKeyAlgorithm) { ASSERT(exec->hadException()); return jsUndefined(); } unwrappedKeyAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(exec, unwrappedKeyAlgorithm->identifier(), exec->uncheckedArgument(4)); if (!unwrappedKeyAlgorithmParameters) { ASSERT(exec->hadException()); return jsUndefined(); } } bool extractable = false; if (exec->argumentCount() >= 6) { extractable = exec->uncheckedArgument(5).toBoolean(exec); if (exec->hadException()) return jsUndefined(); } CryptoKeyUsage keyUsages = 0; if (exec->argumentCount() >= 7) { if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(6), keyUsages)) { ASSERT(exec->hadException()); return jsUndefined(); } } JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject()); DeferredWrapper wrapper(exec, globalObject(), promiseDeferred); Strong<JSDOMGlobalObject> domGlobalObject(exec->vm(), globalObject()); CryptoAlgorithm* unwrappedKeyAlgorithmPtr = unwrappedKeyAlgorithm.release(); CryptoAlgorithmParameters* unwrappedKeyAlgorithmParametersPtr = unwrappedKeyAlgorithmParameters.release(); auto decryptSuccessCallback = [domGlobalObject, keyFormat, unwrappedKeyAlgorithmPtr, unwrappedKeyAlgorithmParametersPtr, extractable, keyUsages, wrapper](const Vector<uint8_t>& result) mutable { auto importSuccessCallback = [wrapper](CryptoKey& key) mutable { wrapper.resolve(&key); }; auto importFailureCallback = [wrapper]() mutable { wrapper.reject(nullptr); }; ExecState* exec = domGlobalObject->globalExec(); WebCore::importKey(exec, keyFormat, std::make_pair(result.data(), result.size()), std::unique_ptr<CryptoAlgorithm>(unwrappedKeyAlgorithmPtr), std::unique_ptr<CryptoAlgorithmParameters>(unwrappedKeyAlgorithmParametersPtr), extractable, keyUsages, WTF::move(importSuccessCallback), WTF::move(importFailureCallback)); if (exec->hadException()) { // 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. exec->clearException(); importFailureCallback(); } }; auto decryptFailureCallback = [wrapper, unwrappedKeyAlgorithmPtr, unwrappedKeyAlgorithmParametersPtr]() mutable { delete unwrappedKeyAlgorithmPtr; delete unwrappedKeyAlgorithmParametersPtr; wrapper.reject(nullptr); }; ExceptionCode ec = 0; unwrapAlgorithm->decryptForUnwrapKey(*unwrapAlgorithmParameters, *unwrappingKey, wrappedKeyData, WTF::move(decryptSuccessCallback), WTF::move(decryptFailureCallback), ec); if (ec) { delete unwrappedKeyAlgorithmPtr; delete unwrappedKeyAlgorithmParametersPtr; setDOMException(exec, ec); return jsUndefined(); } return promiseDeferred->promise(); }
void JSLazyEventListener::parseCode(ScriptExecutionContext* executionContext) const { ASSERT(executionContext); ASSERT(executionContext->isDocument()); if (!executionContext) return; if (m_parsed) return; Frame* frame = static_cast<Document*>(executionContext)->frame(); if (!frame) return; ScriptController* scriptController = frame->script(); if (!scriptController->isEnabled()) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext); if (!globalObject) return; // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating. if (m_originalNode) { JSLock lock(SilenceAssertionsOnly); // FIXME: Should pass the global object associated with the node toJS(globalObject->globalExec(), globalObject, m_originalNode); } if (executionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } m_parsed = true; ExecState* exec = globalObject->globalExec(); MarkedArgumentBuffer args; args.append(jsNontrivialString(exec, m_eventParameterName)); args.append(jsString(exec, m_code)); m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), m_sourceURL, m_lineNumber); // FIXME: is globalExec ok? JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction); if (exec->hadException()) { exec->clearException(); // failed to parse, so let's just make this listener a no-op m_jsFunction = 0; } else if (m_originalNode) { // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); JSValue thisObj = toJS(exec, globalObject, m_originalNode); if (thisObj.isObject()) { static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } } // Since we only parse once, there's no need to keep data used for parsing around anymore. m_functionName = String(); m_code = String(); m_eventParameterName = String(); m_sourceURL = String(); }
JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const { ASSERT(is<Document>(executionContext)); if (!executionContext) return nullptr; ASSERT(!m_code.isNull()); ASSERT(!m_eventParameterName.isNull()); if (m_code.isNull() || m_eventParameterName.isNull()) return nullptr; Document& document = downcast<Document>(*executionContext); if (!document.frame()) return nullptr; if (!document.contentSecurityPolicy()->allowInlineEventHandlers(m_sourceURL, m_sourcePosition.m_line)) return nullptr; ScriptController& script = document.frame()->script(); if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused()) return nullptr; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld()); if (!globalObject) return nullptr; ExecState* exec = globalObject->globalExec(); MarkedArgumentBuffer args; args.append(jsNontrivialString(exec, m_eventParameterName)); args.append(jsStringWithCache(exec, m_code)); // We want all errors to refer back to the line on which our attribute was // declared, regardless of any newlines in our JavaScript source text. int overrideLineNumber = m_sourcePosition.m_line.oneBasedInt(); JSObject* jsFunction = constructFunctionSkippingEvalEnabledCheck( exec, exec->lexicalGlobalObject(), args, Identifier::fromString(exec, m_functionName), m_sourceURL, m_sourcePosition, overrideLineNumber); if (exec->hadException()) { reportCurrentException(exec); exec->clearException(); return nullptr; } JSFunction* listenerAsFunction = jsCast<JSFunction*>(jsFunction); if (m_originalNode) { if (!wrapper()) { // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating. JSLockHolder lock(exec); // FIXME: Should pass the global object associated with the node setWrapper(exec->vm(), asObject(toJS(exec, globalObject, *m_originalNode))); } // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) listenerAsFunction->setScope(exec->vm(), jsCast<JSNode*>(wrapper())->pushEventHandlerScope(exec, listenerAsFunction->scope())); } return jsFunction; }
JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const { ASSERT(executionContext); ASSERT(executionContext->isDocument()); if (!executionContext) return 0; Frame* frame = static_cast<Document*>(executionContext)->frame(); if (!frame) return 0; ScriptController* scriptController = frame->script(); if (!scriptController->canExecuteScripts(AboutToExecuteScript)) return 0; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld()); if (!globalObject) return 0; if (executionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return 0; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused()) return 0; } ExecState* exec = globalObject->globalExec(); MarkedArgumentBuffer args; args.append(jsNontrivialString(exec, stringToUString(m_eventParameterName))); args.append(jsString(exec, m_code)); JSObject* jsFunction = constructFunction(exec, args, Identifier(exec, stringToUString(m_functionName)), stringToUString(m_sourceURL), m_lineNumber); // FIXME: is globalExec ok? if (exec->hadException()) { exec->clearException(); return 0; } JSFunction* listenerAsFunction = static_cast<JSFunction*>(jsFunction); if (m_originalNode) { if (!wrapper()) { // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating. JSLock lock(SilenceAssertionsOnly); // FIXME: Should pass the global object associated with the node setWrapper(asObject(toJS(globalObject->globalExec(), globalObject, m_originalNode))); } // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); static_cast<JSNode*>(wrapper())->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } // Since we only parse once, there's no need to keep data used for parsing around anymore. m_functionName = String(); m_code = String(); m_eventParameterName = String(); m_sourceURL = String(); return jsFunction; }
RefPtr<IDBRequest> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck, ExceptionCodeWithMessage& ec) { LOG(IndexedDB, "IDBObjectStore::putOrAdd"); ASSERT(currentThread() == m_transaction->database().originThreadID()); auto context = scriptExecutionContextFromExecState(&state); if (!context) { ec.code = IDBDatabaseException::UnknownError; ec.message = ASCIILiteral("Unable to store record in object store because it does not have a valid script execution context"); return nullptr; } // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before // the exception for an object store being deleted. // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies. // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit. // Until this is sorted out, we'll agree with the test and the majority share browsers. if (m_deleted) { ec.code = IDBDatabaseException::InvalidStateError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The object store has been deleted."); return nullptr; } if (!m_transaction->isActive()) { ec.code = IDBDatabaseException::TransactionInactiveError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is inactive or finished."); return nullptr; } if (m_transaction->isReadOnly()) { ec.code = IDBDatabaseException::ReadOnlyError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is read-only."); return nullptr; } RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(&state, value, nullptr, nullptr); if (state.hadException()) { // Clear the DOM exception from the serializer so we can give a more targeted exception. state.clearException(); ec.code = IDBDatabaseException::DataCloneError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: An object could not be cloned."); return nullptr; } bool privateBrowsingEnabled = false; if (context->isDocument()) { if (auto* page = static_cast<Document*>(context)->page()) privateBrowsingEnabled = page->sessionID().isEphemeral(); } if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) { // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing. ec.code = IDBDatabaseException::DataCloneError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: BlobURLs are not yet supported."); return nullptr; } if (key && !key->isValid()) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The parameter is not a valid key."); return nullptr; } bool usesInlineKeys = !m_info.keyPath().isNull(); bool usesKeyGenerator = autoIncrement(); if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) { if (key) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided."); return nullptr; } RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath()); if (keyPathKey && !keyPathKey->isValid()) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path yielded a value that is not a valid key."); return nullptr; } if (!keyPathKey) { if (usesKeyGenerator) { if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath())) { ec.code = IDBDatabaseException::DataError; return nullptr; } } else { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value."); return nullptr; } } if (keyPathKey) { ASSERT(!key); key = keyPathKey; } } else if (!usesKeyGenerator && !key) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses out-of-line keys and has no key generator and the key parameter was not provided."); return nullptr; } return m_transaction->requestPutOrAdd(*context, *this, key.get(), *serializedValue, overwriteMode); }
void JSAbstractEventListener::handleEvent(Event* ele, bool isWindowEvent) { #ifdef KJS_DEBUGGER if (KJSDebugWin::instance() && KJSDebugWin::instance()->inSession()) return; #endif Event *event = ele; JSObject* listener = listenerObj(); if (!listener) return; Window* window = windowObj(); Frame *frame = window->frame(); if (!frame) return; KJSProxy* proxy = frame->jScript(); if (!proxy) return; JSLock lock; ScriptInterpreter* interpreter = proxy->interpreter(); ExecState* exec = interpreter->globalExec(); JSValue* handleEventFuncValue = listener->get(exec, "handleEvent"); JSObject* handleEventFunc = 0; if (handleEventFuncValue->isObject()) { handleEventFunc = static_cast<JSObject*>(handleEventFuncValue); if (!handleEventFunc->implementsCall()) handleEventFunc = 0; } if (handleEventFunc || listener->implementsCall()) { ref(); List args; args.append(toJS(exec, event)); // Set the event we're handling in the Window object window->setCurrentEvent(event); // ... and in the interpreter interpreter->setCurrentEvent(event); JSValue* retval; if (handleEventFunc) { interpreter->startTimeoutCheck(); retval = handleEventFunc->call(exec, listener, args); } else { JSObject* thisObj; if (isWindowEvent) thisObj = window; else thisObj = static_cast<JSObject*>(toJS(exec, event->currentTarget())); interpreter->startTimeoutCheck(); retval = listener->call(exec, thisObj, args); } interpreter->stopTimeoutCheck(); window->setCurrentEvent(0); interpreter->setCurrentEvent(0); if (exec->hadException()) { JSObject* exception = exec->exception()->toObject(exec); String message = exception->get(exec, messagePropertyName)->toString(exec); int lineNumber = exception->get(exec, "line")->toInt32(exec); String sourceURL = exception->get(exec, "sourceURL")->toString(exec); if (Interpreter::shouldPrintExceptions()) printf("(event handler):%s\n", message.deprecatedString().utf8().data()); frame->addMessageToConsole(message, lineNumber, sourceURL); exec->clearException(); } else { if (!retval->isUndefinedOrNull() && event->storesResultAsString()) event->storeResult(retval->toString(exec)); if (html) { bool retvalbool; if (retval->getBoolean(retvalbool) && !retvalbool) event->preventDefault(); } } Document::updateDocumentsRendering(); deref(); } }
void JSLazyEventListener::parseCode() const { if (!parsed) { KHTMLPart *part = qobject_cast<KHTMLPart*>(static_cast<Window*>(win.get())->part()); KJSProxy *proxy = 0L; if (part) proxy = part->jScript(); if (proxy) { KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(proxy->interpreter()); ExecState *exec = interpreter->globalExec(); //KJS::Constructor constr(KJS::Global::current().get("Function").imp()); KJS::FunctionObjectImp *constr = static_cast<KJS::FunctionObjectImp*>(interpreter->builtinFunction()); KJS::List args; if (svg) args.append(jsString("evt")); else args.append(jsString("event")); args.append(jsString(code)); listener = constr->construct(exec, args, Identifier(UString(name)), url, lineNum); // ### is globalExec ok ? compareListenerImp = listener; if (exec->hadException()) { exec->clearException(); // failed to parse, so let's just make this listener a no-op listener = 0; } else if (!listener->inherits(&DeclaredFunctionImp::info)) { listener = 0;// Error creating function } else { DeclaredFunctionImp *declFunc = static_cast<DeclaredFunctionImp*>(listener.get()); if (originalNode) { // Add the event's home element to the scope // (and the document, and the form - see KJS::HTMLElement::eventHandlerScope) ScopeChain scope = declFunc->scope(); JSObject *thisObj = getDOMNode(exec, originalNode)->getObject(); if (thisObj) { static_cast<DOMNode*>(thisObj)->pushEventHandlerScope(exec, scope); declFunc->setScope(scope); } } } } // no more need to keep the unparsed code around code.clear(); if (listener) { static_cast<Window*>(win.get())->jsEventListeners.insert(QPair<void*, bool>(compareListenerImp.get(), true), (KJS::JSEventListener *)(this)); } parsed = true; } }