static void populateContextMenuItems(ExecState* exec, JSArray* array, ContextMenu& menu) { for (size_t i = 0; i < array->length(); ++i) { JSObject* item = asObject(array->getIndex(exec, i)); JSValue label = item->get(exec, Identifier::fromString(exec, "label")); JSValue type = item->get(exec, Identifier::fromString(exec, "type")); JSValue id = item->get(exec, Identifier::fromString(exec, "id")); JSValue enabled = item->get(exec, Identifier::fromString(exec, "enabled")); JSValue checked = item->get(exec, Identifier::fromString(exec, "checked")); JSValue subItems = item->get(exec, Identifier::fromString(exec, "subItems")); if (!type.isString()) continue; String typeString = type.toWTFString(exec); if (typeString == "separator") { ContextMenuItem item(SeparatorType, ContextMenuItemTagNoAction, String()); menu.appendItem(item); } else if (typeString == "subMenu" && subItems.inherits(JSArray::info())) { ContextMenu subMenu; JSArray* subItemsArray = asArray(subItems); populateContextMenuItems(exec, subItemsArray, subMenu); ContextMenuItem item(SubmenuType, ContextMenuItemTagNoAction, label.toWTFString(exec), &subMenu); menu.appendItem(item); } else { ContextMenuAction typedId = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + id.toInt32(exec)); ContextMenuItem menuItem((typeString == "checkbox" ? CheckableActionType : ActionType), typedId, label.toWTFString(exec)); if (!enabled.isUndefined()) menuItem.setEnabled(enabled.toBoolean(exec)); if (!checked.isUndefined()) menuItem.setChecked(checked.toBoolean(exec)); menu.appendItem(menuItem); } } }
// Based on ErrorPrototype's errorProtoFuncToString(), but is modified to // have no observable side effects to the user (i.e. does not call proxies, // and getters). String ErrorInstance::sanitizedToString(ExecState* exec) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSValue nameValue; auto namePropertName = vm.propertyNames->name; PropertySlot nameSlot(this, PropertySlot::InternalMethodType::VMInquiry); JSValue currentObj = this; unsigned prototypeDepth = 0; // We only check the current object and its prototype (2 levels) because normal // Error objects may have a name property, and if not, its prototype should have // a name property for the type of error e.g. "SyntaxError". while (currentObj.isCell() && prototypeDepth++ < 2) { JSObject* obj = jsCast<JSObject*>(currentObj); if (JSObject::getOwnPropertySlot(obj, exec, namePropertName, nameSlot) && nameSlot.isValue()) { nameValue = nameSlot.getValue(exec, namePropertName); break; } currentObj = obj->getPrototypeDirect(); } ASSERT(!scope.exception()); String nameString; if (!nameValue) nameString = ASCIILiteral("Error"); else { nameString = nameValue.toWTFString(exec); RETURN_IF_EXCEPTION(scope, String()); } JSValue messageValue; auto messagePropertName = vm.propertyNames->message; PropertySlot messageSlot(this, PropertySlot::InternalMethodType::VMInquiry); if (JSObject::getOwnPropertySlot(this, exec, messagePropertName, messageSlot) && messageSlot.isValue()) messageValue = messageSlot.getValue(exec, messagePropertName); ASSERT(!scope.exception()); String messageString; if (!messageValue) messageString = String(); else { messageString = messageValue.toWTFString(exec); RETURN_IF_EXCEPTION(scope, String()); } if (!nameString.length()) return messageString; if (!messageString.length()) return nameString; StringBuilder builder; builder.append(nameString); builder.appendLiteral(": "); builder.append(messageString); return builder.toString(); }
// ECMA-262 5.1, 15.11.4.4 EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); // 1. Let O be the this value. JSValue thisValue = exec->thisValue(); // 2. If Type(O) is not Object, throw a TypeError exception. if (!thisValue.isObject()) return throwVMTypeError(exec, scope); JSObject* thisObj = asObject(thisValue); // Guard against recursion! StringRecursionChecker checker(exec, thisObj); ASSERT(!scope.exception() || checker.earlyReturnValue()); if (JSValue earlyReturnValue = checker.earlyReturnValue()) return JSValue::encode(earlyReturnValue); // 3. Let name be the result of calling the [[Get]] internal method of O with argument "name". JSValue name = thisObj->get(exec, exec->propertyNames().name); RETURN_IF_EXCEPTION(scope, encodedJSValue()); // 4. If name is undefined, then let name be "Error"; else let name be ToString(name). String nameString; if (name.isUndefined()) nameString = ASCIILiteral("Error"); else { nameString = name.toWTFString(exec); RETURN_IF_EXCEPTION(scope, encodedJSValue()); } // 5. Let msg be the result of calling the [[Get]] internal method of O with argument "message". JSValue message = thisObj->get(exec, exec->propertyNames().message); RETURN_IF_EXCEPTION(scope, encodedJSValue()); // (sic) // 6. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). // 7. If msg is undefined, then let msg be the empty String; else let msg be ToString(msg). String messageString; if (message.isUndefined()) messageString = String(); else { messageString = message.toWTFString(exec); RETURN_IF_EXCEPTION(scope, encodedJSValue()); } // 8. If name is the empty String, return msg. if (!nameString.length()) return JSValue::encode(message.isString() ? message : jsString(exec, messageString)); // 9. If msg is the empty String, return name. if (!messageString.length()) return JSValue::encode(name.isString() ? name : jsString(exec, nameString)); // 10. Return the result of concatenating name, ":", a single space character, and msg. scope.release(); return JSValue::encode(jsMakeNontrivialString(exec, nameString, ": ", messageString)); }
void JSLocation::setPort(ExecState* exec, JSValue value) { String port = value.toWTFString(exec); if (exec->hadException()) return; impl()->setPort(port, activeDOMWindow(exec), firstDOMWindow(exec)); }
bool JSStorage::putDelegate(ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot&, bool& putResult) { VM& vm = state->vm(); auto scope = DECLARE_THROW_SCOPE(vm); // Only perform the custom put if the object doesn't have a native property by this name. // Since hasProperty() would end up calling canGetItemsForName() and be fooled, we need to check // the native property slots manually. PropertySlot slot { this, PropertySlot::InternalMethodType::GetOwnProperty }; JSValue prototype = this->getPrototypeDirect(); if (prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot)) return false; if (propertyName.isSymbol()) return false; String stringValue = value.toWTFString(state); RETURN_IF_EXCEPTION(scope, true); auto setItemResult = wrapped().setItem(propertyNameToString(propertyName), stringValue); if (setItemResult.hasException()) { propagateException(*state, scope, setItemResult.releaseException()); return true; } putResult = true; return true; }
static std::error_code getTypeFlags(ExecState& exec, const JSValue& typeValue, ResourceFlags& flags, uint16_t (*stringToType)(const String&)) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (!typeValue.isObject()) return { }; const JSObject* object = typeValue.toObject(&exec); ASSERT(!scope.exception()); if (!isJSArray(object)) return ContentExtensionError::JSONInvalidTriggerFlagsArray; const JSArray* array = jsCast<const JSArray*>(object); unsigned length = array->length(); for (unsigned i = 0; i < length; ++i) { const JSValue value = array->getIndex(&exec, i); if (scope.exception() || !value) return ContentExtensionError::JSONInvalidObjectInTriggerFlagsArray; String name = value.toWTFString(&exec); uint16_t type = stringToType(name); if (!type) return ContentExtensionError::JSONInvalidStringInTriggerFlagsArray; flags |= type; } return { }; }
static bool extractSourceInformationFromException(JSC::ExecState* exec, JSObject* exceptionObject, int* lineNumber, int* columnNumber, String* sourceURL) { VM& vm = exec->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); // FIXME: <http://webkit.org/b/115087> Web Inspector: Should not need to evaluate JavaScript handling exceptions JSValue lineValue = exceptionObject->getDirect(vm, Identifier::fromString(exec, "line")); JSValue columnValue = exceptionObject->getDirect(vm, Identifier::fromString(exec, "column")); JSValue sourceURLValue = exceptionObject->getDirect(vm, Identifier::fromString(exec, "sourceURL")); bool result = false; if (lineValue && lineValue.isNumber() && sourceURLValue && sourceURLValue.isString()) { *lineNumber = int(lineValue.toNumber(exec)); *columnNumber = columnValue && columnValue.isNumber() ? int(columnValue.toNumber(exec)) : 0; *sourceURL = sourceURLValue.toWTFString(exec); result = true; } else if (ErrorInstance* error = jsDynamicCast<ErrorInstance*>(vm, exceptionObject)) { unsigned unsignedLine; unsigned unsignedColumn; result = getLineColumnAndSource(error->stackTrace(), unsignedLine, unsignedColumn, *sourceURL); *lineNumber = static_cast<int>(unsignedLine); *columnNumber = static_cast<int>(unsignedColumn); } if (sourceURL->isEmpty()) *sourceURL = "undefined"_s; scope.clearException(); return result; }
template<> Optional<TestStandaloneDictionary::EnumInStandaloneDictionaryFile> parseEnumeration<TestStandaloneDictionary::EnumInStandaloneDictionaryFile>(ExecState& state, JSValue value) { auto stringValue = value.toWTFString(&state); if (stringValue == "enumValue1") return TestStandaloneDictionary::EnumInStandaloneDictionaryFile::EnumValue1; if (stringValue == "enumValue2") return TestStandaloneDictionary::EnumInStandaloneDictionaryFile::EnumValue2; return WTF::nullopt; }
bool setJSTestInterfaceConstructorSupplementalStaticAttr(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); auto nativeValue = value.toWTFString(state); if (UNLIKELY(state->hadException())) return false; WebCore::TestSupplemental::setSupplementalStaticAttr(WTFMove(nativeValue)); return true; }
bool JSDictionary::getWithUndefinedOrNullCheck(const String& propertyName, String& result) const { ASSERT(isValid()); JSValue value; if (tryGetProperty(propertyName.utf8().data(), value) != PropertyFound || value.isUndefinedOrNull()) return false; result = value.toWTFString(m_exec); return true; }
static std::error_code loadAction(ExecState& exec, const JSObject& ruleObject, Action& action, bool& validSelector) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); validSelector = true; const JSValue actionObject = ruleObject.get(&exec, Identifier::fromString(&exec, "action")); if (!actionObject || scope.exception() || !actionObject.isObject()) return ContentExtensionError::JSONInvalidAction; const JSValue typeObject = actionObject.get(&exec, Identifier::fromString(&exec, "type")); if (!typeObject || scope.exception() || !typeObject.isString()) return ContentExtensionError::JSONInvalidActionType; String actionType = typeObject.toWTFString(&exec); if (actionType == "block") action = ActionType::BlockLoad; else if (actionType == "ignore-previous-rules") action = ActionType::IgnorePreviousRules; else if (actionType == "block-cookies") action = ActionType::BlockCookies; else if (actionType == "css-display-none") { JSValue selector = actionObject.get(&exec, Identifier::fromString(&exec, "selector")); if (!selector || scope.exception() || !selector.isString()) return ContentExtensionError::JSONInvalidCSSDisplayNoneActionType; String s = selector.toWTFString(&exec); if (!isValidSelector(s)) { // Skip rules with invalid selectors to be backwards-compatible. validSelector = false; return { }; } action = Action(ActionType::CSSDisplayNoneSelector, s); } else if (actionType == "make-https") { action = ActionType::MakeHTTPS; } else return ContentExtensionError::JSONInvalidActionType; return { }; }
bool setJSTestInterfaceSupplementalStr2(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); UNUSED_PARAM(thisValue); JSTestInterface* castedThis = jsDynamicCast<JSTestInterface*>(JSValue::decode(thisValue)); if (UNLIKELY(!castedThis)) { return throwSetterTypeError(*state, "TestInterface", "supplementalStr2"); } auto& impl = castedThis->wrapped(); auto nativeValue = value.toWTFString(state); if (UNLIKELY(state->hadException())) return false; WebCore::TestSupplemental::setSupplementalStr2(impl, WTFMove(nativeValue)); return true; }
bool setJSTestNodeName(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); UNUSED_PARAM(thisValue); JSTestNode* castedThis = jsDynamicCast<JSTestNode*>(JSValue::decode(thisValue)); if (UNLIKELY(!castedThis)) { return throwSetterTypeError(*state, "TestNode", "name"); } auto& impl = castedThis->wrapped(); auto nativeValue = value.toWTFString(state); if (UNLIKELY(state->hadException())) return false; impl.setName(WTFMove(nativeValue)); return true; }
bool setJSTestGlobalObjectPublicAndPrivateConditionalAttribute(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); UNUSED_PARAM(thisValue); JSTestGlobalObject* castedThis = jsDynamicCast<JSTestGlobalObject*>(JSValue::decode(thisValue)); if (UNLIKELY(!castedThis)) { return throwSetterTypeError(*state, "TestGlobalObject", "publicAndPrivateConditionalAttribute"); } auto& impl = castedThis->wrapped(); auto nativeValue = value.toWTFString(state); if (UNLIKELY(state->hadException())) return false; impl.setPublicAndPrivateConditionalAttribute(WTFMove(nativeValue)); return true; }
static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) { VM& vm = exec->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); JSObject* memoryDescriptor; { JSValue argument = exec->argument(0); if (!argument.isObject()) return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its first argument to be an object")))); memoryDescriptor = jsCast<JSObject*>(argument); } { Identifier elementIdent = Identifier::fromString(&vm, "element"); JSValue elementValue = memoryDescriptor->get(exec, elementIdent); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); String elementString = elementValue.toWTFString(exec); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); if (elementString != "anyfunc") return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its 'element' field to be the string 'anyfunc'")))); } Identifier initialIdent = Identifier::fromString(&vm, "initial"); JSValue initialSizeValue = memoryDescriptor->get(exec, initialIdent); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); uint32_t initial = toNonWrappingUint32(exec, initialSizeValue); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); std::optional<uint32_t> maximum; Identifier maximumIdent = Identifier::fromString(&vm, "maximum"); bool hasProperty = memoryDescriptor->hasProperty(exec, maximumIdent); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); if (hasProperty) { JSValue maxSizeValue = memoryDescriptor->get(exec, maximumIdent); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); maximum = toNonWrappingUint32(exec, maxSizeValue); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); if (initial > *maximum) { return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("'maximum' property must be greater than or equal to the 'initial' property")))); } } throwScope.release(); return JSValue::encode(JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(), initial, maximum)); }
static CryptoKeyFormat cryptoKeyFormatFromJSValue(ExecState& state, ThrowScope& scope, JSValue value) { auto keyFormatString = value.toWTFString(&state); RETURN_IF_EXCEPTION(scope, { }); if (keyFormatString == "raw") return CryptoKeyFormat::Raw; if (keyFormatString == "pkcs8") return CryptoKeyFormat::PKCS8; if (keyFormatString == "spki") return CryptoKeyFormat::SPKI; if (keyFormatString == "jwk") return CryptoKeyFormat::JWK; throwTypeError(&state, scope, ASCIILiteral("Unknown key format")); return { }; }
String JSCustomXPathNSResolver::lookupNamespaceURI(const String& prefix) { ASSERT(m_customResolver); JSLockHolder lock(commonVM()); ExecState* exec = m_globalObject->globalExec(); JSValue function = m_customResolver->get(exec, Identifier::fromString(exec, "lookupNamespaceURI")); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallType::None) { callType = m_customResolver->methodTable()->getCallData(m_customResolver.get(), callData); if (callType == CallType::None) { if (PageConsoleClient* console = m_globalObject->wrapped().console()) console->addMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("XPathNSResolver does not have a lookupNamespaceURI method.")); return String(); } function = m_customResolver.get(); } Ref<JSCustomXPathNSResolver> protectedThis(*this); MarkedArgumentBuffer args; args.append(jsStringWithCache(exec, prefix)); NakedPtr<JSC::Exception> exception; JSValue retval = JSMainThreadExecState::call(exec, function, callType, callData, m_customResolver.get(), args, exception); String result; if (exception) reportException(exec, exception); else { if (!retval.isUndefinedOrNull()) result = retval.toWTFString(exec); } return result; }
static std::error_code loadTrigger(ExecState& exec, const JSObject& ruleObject, Trigger& trigger) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); const JSValue triggerObject = ruleObject.get(&exec, Identifier::fromString(&exec, "trigger")); if (!triggerObject || scope.exception() || !triggerObject.isObject()) return ContentExtensionError::JSONInvalidTrigger; const JSValue urlFilterObject = triggerObject.get(&exec, Identifier::fromString(&exec, "url-filter")); if (!urlFilterObject || scope.exception() || !urlFilterObject.isString()) return ContentExtensionError::JSONInvalidURLFilterInTrigger; String urlFilter = urlFilterObject.toWTFString(&exec); if (urlFilter.isEmpty()) return ContentExtensionError::JSONInvalidURLFilterInTrigger; trigger.urlFilter = urlFilter; const JSValue urlFilterCaseValue = triggerObject.get(&exec, Identifier::fromString(&exec, "url-filter-is-case-sensitive")); if (urlFilterCaseValue && !scope.exception() && urlFilterCaseValue.isBoolean()) trigger.urlFilterIsCaseSensitive = urlFilterCaseValue.toBoolean(&exec); const JSValue resourceTypeValue = triggerObject.get(&exec, Identifier::fromString(&exec, "resource-type")); if (!scope.exception() && resourceTypeValue.isObject()) { auto typeFlagsError = getTypeFlags(exec, resourceTypeValue, trigger.flags, readResourceType); if (typeFlagsError) return typeFlagsError; } else if (!resourceTypeValue.isUndefined()) return ContentExtensionError::JSONInvalidTriggerFlagsArray; const JSValue loadTypeValue = triggerObject.get(&exec, Identifier::fromString(&exec, "load-type")); if (!scope.exception() && loadTypeValue.isObject()) { auto typeFlagsError = getTypeFlags(exec, loadTypeValue, trigger.flags, readLoadType); if (typeFlagsError) return typeFlagsError; } else if (!loadTypeValue.isUndefined()) return ContentExtensionError::JSONInvalidTriggerFlagsArray; const JSValue ifDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "if-domain")); if (!scope.exception() && ifDomain.isObject()) { auto ifDomainError = getDomainList(exec, asObject(ifDomain), trigger.domains); if (ifDomainError) return ifDomainError; if (trigger.domains.isEmpty()) return ContentExtensionError::JSONInvalidDomainList; ASSERT(trigger.domainCondition == Trigger::DomainCondition::None); trigger.domainCondition = Trigger::DomainCondition::IfDomain; } else if (!ifDomain.isUndefined()) return ContentExtensionError::JSONInvalidDomainList; const JSValue unlessDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "unless-domain")); if (!scope.exception() && unlessDomain.isObject()) { if (trigger.domainCondition != Trigger::DomainCondition::None) return ContentExtensionError::JSONUnlessAndIfDomain; auto unlessDomainError = getDomainList(exec, asObject(unlessDomain), trigger.domains); if (unlessDomainError) return unlessDomainError; if (trigger.domains.isEmpty()) return ContentExtensionError::JSONInvalidDomainList; trigger.domainCondition = Trigger::DomainCondition::UnlessDomain; } else if (!unlessDomain.isUndefined()) return ContentExtensionError::JSONInvalidDomainList; return { }; }
EncodedJSValue JSC_HOST_CALL constructJSFile(ExecState& exec) { auto* constructor = jsCast<DOMConstructorObject*>(exec.callee()); ScriptExecutionContext* context = constructor->scriptExecutionContext(); if (!context) return throwVMError(&exec, createReferenceError(&exec, "File constructor associated document is unavailable")); JSValue arg = exec.argument(0); if (arg.isUndefinedOrNull()) return throwVMTypeError(&exec, ASCIILiteral("First argument to File constructor must be a valid sequence, was undefined or null")); unsigned blobPartsLength = 0; JSObject* blobParts = toJSSequence(exec, arg, blobPartsLength); if (exec.hadException()) return JSValue::encode(jsUndefined()); ASSERT(blobParts); arg = exec.argument(1); if (arg.isUndefined()) return throwVMTypeError(&exec, ASCIILiteral("Second argument to File constructor must be a valid string, was undefined")); String filename = arg.toWTFString(&exec).replace('/', ':'); if (exec.hadException()) return JSValue::encode(jsUndefined()); String normalizedType; Optional<int64_t> lastModified; arg = exec.argument(2); if (!arg.isUndefinedOrNull()) { JSObject* filePropertyBagObject = arg.getObject(); if (!filePropertyBagObject) return throwVMTypeError(&exec, ASCIILiteral("Third argument of the constructor is not of type Object")); // Create the dictionary wrapper from the initializer object. JSDictionary dictionary(&exec, filePropertyBagObject); // Attempt to get the type property. String type; dictionary.get("type", type); if (exec.hadException()) return JSValue::encode(jsUndefined()); normalizedType = Blob::normalizedContentType(type); // Only try to parse the lastModified date if there was not an invalid type argument. if (type.isEmpty() || !normalizedType.isEmpty()) { dictionary.get("lastModified", lastModified); if (exec.hadException()) return JSValue::encode(jsUndefined()); } } if (!lastModified) lastModified = currentTimeMS(); BlobBuilder blobBuilder; for (unsigned i = 0; i < blobPartsLength; ++i) { JSValue item = blobParts->get(&exec, i); if (exec.hadException()) return JSValue::encode(jsUndefined()); if (ArrayBuffer* arrayBuffer = toArrayBuffer(item)) blobBuilder.append(arrayBuffer); else if (RefPtr<ArrayBufferView> arrayBufferView = toArrayBufferView(item)) blobBuilder.append(WTFMove(arrayBufferView)); else if (Blob* blob = JSBlob::toWrapped(item)) blobBuilder.append(blob); else { String string = item.toWTFString(&exec); if (exec.hadException()) return JSValue::encode(jsUndefined()); blobBuilder.append(string, ASCIILiteral("transparent")); } } auto file = File::create(blobBuilder.finalize(), filename, normalizedType, lastModified.value()); return JSValue::encode(CREATE_DOM_WRAPPER(constructor->globalObject(), File, WTFMove(file))); }
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { ASSERT(scriptExecutionContext); if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden()) return; VM& vm = scriptExecutionContext->vm(); JSLockHolder lock(vm); auto scope = DECLARE_CATCH_SCOPE(vm); // See https://dom.spec.whatwg.org/#dispatching-events spec on calling handleEvent. // "If this throws an exception, report the exception." It should not propagate the // exception. JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld); if (!globalObject) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject); if (!window->wrapped().isCurrentlyDisplayedInFrame()) return; if (wasCreatedFromMarkup() && !scriptExecutionContext->contentSecurityPolicy()->allowInlineEventHandlers(sourceURL(), sourcePosition().m_line)) return; // FIXME: Is this check needed for other contexts? ScriptController& script = window->wrapped().frame()->script(); if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction; CallData callData; CallType callType = getCallData(handleEventFunction, callData); // If jsFunction is not actually a function, see if it implements the EventListener interface and use that if (callType == CallType::None) { handleEventFunction = jsFunction->get(exec, Identifier::fromString(exec, "handleEvent")); if (UNLIKELY(scope.exception())) { auto* exception = scope.exception(); scope.clearException(); event->target()->uncaughtExceptionInEventHandler(); reportException(exec, exception); return; } callType = getCallData(handleEventFunction, callData); } if (callType != CallType::None) { Ref<JSEventListener> protectedThis(*this); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject); InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData); JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction; NakedPtr<JSC::Exception> exception; JSValue retval = scriptExecutionContext->isDocument() ? JSMainThreadExecState::profiledCall(exec, JSC::ProfilingReason::Other, handleEventFunction, callType, callData, thisValue, args, exception) : JSC::profiledCall(exec, JSC::ProfilingReason::Other, handleEventFunction, callType, callData, thisValue, args, exception); InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext); globalObject->setCurrentEvent(savedEvent); if (is<WorkerGlobalScope>(*scriptExecutionContext)) { auto scriptController = downcast<WorkerGlobalScope>(*scriptExecutionContext).script(); bool terminatorCausedException = (scope.exception() && isTerminatedExecutionException(scope.exception())); if (terminatorCausedException || scriptController->isTerminatingExecution()) scriptController->forbidExecution(); } if (exception) { event->target()->uncaughtExceptionInEventHandler(); reportException(exec, exception); } else { if (!retval.isUndefinedOrNull() && is<BeforeUnloadEvent>(*event)) downcast<BeforeUnloadEvent>(*event).setReturnValue(retval.toWTFString(exec)); if (m_isAttribute) { if (retval.isFalse()) event->preventDefault(); } } } }
static String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value) { if (value.isUndefinedOrNull()) return String(); return value.toWTFString(exec); }
static String valueOrDefaultLabelString(ExecState* exec, JSValue value) { if (value.isUndefined()) return "default"_s; return value.toWTFString(exec); }
void IntlDateTimeFormat::initializeDateTimeFormat(ExecState& exec, JSValue locales, JSValue originalOptions) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); // 12.1.1 InitializeDateTimeFormat (dateTimeFormat, locales, options) (ECMA-402 2.0) // 1. If dateTimeFormat.[[initializedIntlObject]] is true, throw a TypeError exception. // 2. Set dateTimeFormat.[[initializedIntlObject]] to true. // 3. Let requestedLocales be CanonicalizeLocaleList(locales). Vector<String> requestedLocales = canonicalizeLocaleList(exec, locales); // 4. ReturnIfAbrupt(requestedLocales), RETURN_IF_EXCEPTION(scope, void()); // 5. Let options be ToDateTimeOptions(options, "any", "date"). JSObject* options = toDateTimeOptionsAnyDate(exec, originalOptions); // 6. ReturnIfAbrupt(options). RETURN_IF_EXCEPTION(scope, void()); // 7. Let opt be a new Record. HashMap<String, String> localeOpt; // 8. Let matcher be GetOption(options, "localeMatcher", "string", «"lookup", "best fit"», "best fit"). String localeMatcher = intlStringOption(exec, options, vm.propertyNames->localeMatcher, { "lookup", "best fit" }, "localeMatcher must be either \"lookup\" or \"best fit\"", "best fit"); // 9. ReturnIfAbrupt(matcher). RETURN_IF_EXCEPTION(scope, void()); // 10. Set opt.[[localeMatcher]] to matcher. localeOpt.add(vm.propertyNames->localeMatcher.string(), localeMatcher); // 11. Let localeData be the value of %DateTimeFormat%.[[localeData]]. // 12. Let r be ResolveLocale( %DateTimeFormat%.[[availableLocales]], requestedLocales, opt, %DateTimeFormat%.[[relevantExtensionKeys]], localeData). const HashSet<String> availableLocales = exec.jsCallee()->globalObject()->intlDateTimeFormatAvailableLocales(); HashMap<String, String> resolved = resolveLocale(exec, availableLocales, requestedLocales, localeOpt, relevantExtensionKeys, WTF_ARRAY_LENGTH(relevantExtensionKeys), localeData); // 13. Set dateTimeFormat.[[locale]] to the value of r.[[locale]]. m_locale = resolved.get(vm.propertyNames->locale.string()); if (m_locale.isEmpty()) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat due to invalid locale")); return; } // 14. Set dateTimeFormat.[[calendar]] to the value of r.[[ca]]. m_calendar = resolved.get(ASCIILiteral("ca")); // Switch to preferred aliases. if (m_calendar == "gregory") m_calendar = ASCIILiteral("gregorian"); else if (m_calendar == "islamicc") m_calendar = ASCIILiteral("islamic-civil"); else if (m_calendar == "ethioaa") m_calendar = ASCIILiteral("ethiopic-amete-alem"); // 15. Set dateTimeFormat.[[numberingSystem]] to the value of r.[[nu]]. m_numberingSystem = resolved.get(ASCIILiteral("nu")); // 16. Let dataLocale be the value of r.[[dataLocale]]. String dataLocale = resolved.get(ASCIILiteral("dataLocale")); // 17. Let tz be Get(options, "timeZone"). JSValue tzValue = options->get(&exec, vm.propertyNames->timeZone); // 18. ReturnIfAbrupt(tz). RETURN_IF_EXCEPTION(scope, void()); // 19. If tz is not undefined, then String tz; if (!tzValue.isUndefined()) { // a. Let tz be ToString(tz). String originalTz = tzValue.toWTFString(&exec); // b. ReturnIfAbrupt(tz). RETURN_IF_EXCEPTION(scope, void()); // c. If the result of IsValidTimeZoneName(tz) is false, then i. Throw a RangeError exception. // d. Let tz be CanonicalizeTimeZoneName(tz). tz = canonicalizeTimeZoneName(originalTz); if (tz.isNull()) { throwRangeError(&exec, scope, String::format("invalid time zone: %s", originalTz.utf8().data())); return; } } else { // 20. Else, // a. Let tz be DefaultTimeZone(). tz = defaultTimeZone(); } // 21. Set dateTimeFormat.[[timeZone]] to tz. m_timeZone = tz; // 22. Let opt be a new Record. // Rather than building a record, build the skeleton pattern. StringBuilder skeletonBuilder; // 23. For each row of Table 3, except the header row, do: // a. Let prop be the name given in the Property column of the row. // b. Let value be GetOption(options, prop, "string", «the strings given in the Values column of the row», undefined). // c. ReturnIfAbrupt(value). // d. Set opt.[[<prop>]] to value. auto narrowShortLong = { "narrow", "short", "long" }; auto twoDigitNumeric = { "2-digit", "numeric" }; auto twoDigitNumericNarrowShortLong = { "2-digit", "numeric", "narrow", "short", "long" }; auto shortLong = { "short", "long" }; String weekday = intlStringOption(exec, options, vm.propertyNames->weekday, narrowShortLong, "weekday must be \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!weekday.isNull()) { if (weekday == "narrow") skeletonBuilder.appendLiteral("EEEEE"); else if (weekday == "short") skeletonBuilder.appendLiteral("EEE"); else if (weekday == "long") skeletonBuilder.appendLiteral("EEEE"); } String era = intlStringOption(exec, options, vm.propertyNames->era, narrowShortLong, "era must be \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!era.isNull()) { if (era == "narrow") skeletonBuilder.appendLiteral("GGGGG"); else if (era == "short") skeletonBuilder.appendLiteral("GGG"); else if (era == "long") skeletonBuilder.appendLiteral("GGGG"); } String year = intlStringOption(exec, options, vm.propertyNames->year, twoDigitNumeric, "year must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!year.isNull()) { if (year == "2-digit") skeletonBuilder.appendLiteral("yy"); else if (year == "numeric") skeletonBuilder.append('y'); } String month = intlStringOption(exec, options, vm.propertyNames->month, twoDigitNumericNarrowShortLong, "month must be \"2-digit\", \"numeric\", \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!month.isNull()) { if (month == "2-digit") skeletonBuilder.appendLiteral("MM"); else if (month == "numeric") skeletonBuilder.append('M'); else if (month == "narrow") skeletonBuilder.appendLiteral("MMMMM"); else if (month == "short") skeletonBuilder.appendLiteral("MMM"); else if (month == "long") skeletonBuilder.appendLiteral("MMMM"); } String day = intlStringOption(exec, options, vm.propertyNames->day, twoDigitNumeric, "day must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!day.isNull()) { if (day == "2-digit") skeletonBuilder.appendLiteral("dd"); else if (day == "numeric") skeletonBuilder.append('d'); } String hour = intlStringOption(exec, options, vm.propertyNames->hour, twoDigitNumeric, "hour must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); // We need hour12 to make the hour skeleton pattern decision, so do this early. // 32. Let hr12 be GetOption(options, "hour12", "boolean", undefined, undefined). bool isHour12Undefined; bool hr12 = intlBooleanOption(exec, options, vm.propertyNames->hour12, isHour12Undefined); // 33. ReturnIfAbrupt(hr12). RETURN_IF_EXCEPTION(scope, void()); if (!hour.isNull()) { if (isHour12Undefined) { if (hour == "2-digit") skeletonBuilder.appendLiteral("jj"); else if (hour == "numeric") skeletonBuilder.append('j'); } else if (hr12) { if (hour == "2-digit") skeletonBuilder.appendLiteral("hh"); else if (hour == "numeric") skeletonBuilder.append('h'); } else { if (hour == "2-digit") skeletonBuilder.appendLiteral("HH"); else if (hour == "numeric") skeletonBuilder.append('H'); } } String minute = intlStringOption(exec, options, vm.propertyNames->minute, twoDigitNumeric, "minute must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!minute.isNull()) { if (minute == "2-digit") skeletonBuilder.appendLiteral("mm"); else if (minute == "numeric") skeletonBuilder.append('m'); } String second = intlStringOption(exec, options, vm.propertyNames->second, twoDigitNumeric, "second must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!second.isNull()) { if (second == "2-digit") skeletonBuilder.appendLiteral("ss"); else if (second == "numeric") skeletonBuilder.append('s'); } String timeZoneName = intlStringOption(exec, options, vm.propertyNames->timeZoneName, shortLong, "timeZoneName must be \"short\" or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!timeZoneName.isNull()) { if (timeZoneName == "short") skeletonBuilder.append('z'); else if (timeZoneName == "long") skeletonBuilder.appendLiteral("zzzz"); } // 24. Let dataLocaleData be Get(localeData, dataLocale). // 25. Let formats be Get(dataLocaleData, "formats"). // 26. Let matcher be GetOption(options, "formatMatcher", "string", «"basic", "best fit"», "best fit"). intlStringOption(exec, options, vm.propertyNames->formatMatcher, { "basic", "best fit" }, "formatMatcher must be either \"basic\" or \"best fit\"", "best fit"); // 27. ReturnIfAbrupt(matcher). RETURN_IF_EXCEPTION(scope, void()); // Always use ICU date format generator, rather than our own pattern list and matcher. // Covers steps 28-36. UErrorCode status = U_ZERO_ERROR; UDateTimePatternGenerator* generator = udatpg_open(dataLocale.utf8().data(), &status); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } String skeleton = skeletonBuilder.toString(); StringView skeletonView(skeleton); Vector<UChar, 32> patternBuffer(32); status = U_ZERO_ERROR; auto patternLength = udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternBuffer.size(), &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; patternBuffer.grow(patternLength); udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternLength, &status); } udatpg_close(generator); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } StringView pattern(patternBuffer.data(), patternLength); setFormatsFromPattern(pattern); status = U_ZERO_ERROR; StringView timeZoneView(m_timeZone); m_dateFormat = std::unique_ptr<UDateFormat, UDateFormatDeleter>(udat_open(UDAT_PATTERN, UDAT_PATTERN, m_locale.utf8().data(), timeZoneView.upconvertedCharacters(), timeZoneView.length(), pattern.upconvertedCharacters(), pattern.length(), &status)); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } // 37. Set dateTimeFormat.[[boundFormat]] to undefined. // Already undefined. // 38. Set dateTimeFormat.[[initializedDateTimeFormat]] to true. m_initializedDateTimeFormat = true; // 39. Return dateTimeFormat. }