/* * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2023, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include namespace Web::CSS { // https://www.w3.org/TR/cssom-1/#dom-css-escape WebIDL::ExceptionOr escape(JS::VM&, StringView identifier) { // The escape(ident) operation must return the result of invoking serialize an identifier of ident. return serialize_an_identifier(identifier); } // https://www.w3.org/TR/css-conditional-3/#dom-css-supports bool supports(JS::VM&, StringView property, StringView value) { // 1. If property is an ASCII case-insensitive match for any defined CSS property that the UA supports, // and value successfully parses according to that property’s grammar, return true. if (auto property_id = property_id_from_string(property); property_id.has_value()) { if (parse_css_value(Parser::ParsingParams {}, value, property_id.value())) return true; } // 2. Otherwise, if property is a custom property name string, return true. else if (is_a_custom_property_name_string(property)) { return true; } // 3. Otherwise, return false. return false; } // https://www.w3.org/TR/css-conditional-3/#dom-css-supports WebIDL::ExceptionOr supports(JS::VM& vm, StringView condition_text) { auto& realm = *vm.current_realm(); // 1. If conditionText, parsed and evaluated as a , would return true, return true. if (auto supports = parse_css_supports(Parser::ParsingParams { realm }, condition_text); supports && supports->matches()) return true; // 2. Otherwise, If conditionText, wrapped in parentheses and then parsed and evaluated as a , would return true, return true. auto wrapped_condition_text = TRY_OR_THROW_OOM(vm, String::formatted("({})", condition_text)); if (auto supports = parse_css_supports(Parser::ParsingParams { realm }, wrapped_condition_text); supports && supports->matches()) return true; // 3. Otherwise, return false. return false; } // https://www.w3.org/TR/css-properties-values-api-1/#the-registerproperty-function WebIDL::ExceptionOr register_property(JS::VM& vm, PropertyDefinition definition) { // 1. Let property set be the value of the current global object’s associated Document’s [[registeredPropertySet]] slot. auto& realm = *vm.current_realm(); auto& window = static_cast(realm.global_object()); auto& document = window.associated_document(); // 2. If name is not a custom property name string, throw a SyntaxError and exit this algorithm. if (!is_a_custom_property_name_string(definition.name)) return WebIDL::SyntaxError::create(realm, "Invalid property name"_utf16); // If property set already contains an entry with name as its property name (compared codepoint-wise), // throw an InvalidModificationError and exit this algorithm. if (document.registered_custom_properties().contains(definition.name)) return WebIDL::InvalidModificationError::create(realm, "Property already registered"_utf16); auto parsing_params = CSS::Parser::ParsingParams { document }; // 3. Attempt to consume a syntax definition from syntax. If it returns failure, throw a SyntaxError. // Otherwise, let syntax definition be the returned syntax definition. auto syntax_component_values = parse_component_values_list(parsing_params, definition.syntax); auto maybe_syntax = parse_as_syntax(syntax_component_values); if (!maybe_syntax) { return WebIDL::SyntaxError::create(realm, "Invalid syntax definition"_utf16); } RefPtr initial_value_maybe; // 4. If syntax definition is the universal syntax definition, and initialValue is not present, if (maybe_syntax->type() == Parser::SyntaxNode::NodeType::Universal) { if (!definition.initial_value.has_value()) { // let parsed initial value be empty. // This must be treated identically to the "default" initial value of custom properties, as defined in [css-variables]. initial_value_maybe = nullptr; } else { // Otherwise, if syntax definition is the universal syntax definition, // parse initialValue as a initial_value_maybe = parse_css_value(parsing_params, definition.initial_value.value(), PropertyID::Custom); // If this fails, throw a SyntaxError and exit this algorithm. // Otherwise, let parsed initial value be the parsed result. if (!initial_value_maybe) { return WebIDL::SyntaxError::create(realm, "Invalid initial value"_utf16); } } } else if (!definition.initial_value.has_value()) { // Otherwise, if initialValue is not present, throw a SyntaxError and exit this algorithm. return WebIDL::SyntaxError::create(realm, "Initial value must be provided for non-universal syntax"_utf16); } else { // Otherwise, parse initialValue according to syntax definition. auto initial_value_component_values = parse_component_values_list(parsing_params, definition.initial_value.value()); initial_value_maybe = Parser::parse_with_a_syntax( parsing_params, initial_value_component_values, *maybe_syntax); // If this fails, throw a SyntaxError and exit this algorithm. if (!initial_value_maybe || initial_value_maybe->is_guaranteed_invalid()) { return WebIDL::SyntaxError::create(realm, "Invalid initial value"_utf16); } // Otherwise, let parsed initial value be the parsed result. // NB: Already done // FIXME: If parsed initial value is not computationally independent, throw a SyntaxError and exit this algorithm. } // 5. Set inherit flag to the value of inherits. // NB: Combined with 6. // 6. Let registered property be a struct with a property name of name, a syntax of syntax definition, // an initial value of parsed initial value, and an inherit flag of inherit flag. auto registered_property = CSSPropertyRule::create(realm, definition.name, definition.syntax, definition.inherits, initial_value_maybe); // Append registered property to property set. document.registered_custom_properties().set( registered_property->name(), registered_property); return {}; } // https://drafts.css-houdini.org/css-typed-om-1/#numeric-factory inline GC::Ref numeric_factory(JS::VM& vm, WebIDL::Double value, FlyString unit) { // All of the above methods must, when called with a double value, return a new CSSUnitValue whose value internal // slot is set to value and whose unit internal slot is set to the name of the method as defined here. return CSSUnitValue::create(*vm.current_realm(), value, move(unit)); } GC::Ref number(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "number"_fly_string); } GC::Ref percent(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "percent"_fly_string); } // GC::Ref cap(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cap"_fly_string); } GC::Ref ch(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "ch"_fly_string); } GC::Ref em(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "em"_fly_string); } GC::Ref ex(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "ex"_fly_string); } GC::Ref ic(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "ic"_fly_string); } GC::Ref lh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lh"_fly_string); } GC::Ref rcap(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rcap"_fly_string); } GC::Ref rch(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rch"_fly_string); } GC::Ref rem(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rem"_fly_string); } GC::Ref rex(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rex"_fly_string); } GC::Ref ric(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "ric"_fly_string); } GC::Ref rlh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rlh"_fly_string); } GC::Ref vw(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vw"_fly_string); } GC::Ref vh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vh"_fly_string); } GC::Ref vi(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vi"_fly_string); } GC::Ref vb(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vb"_fly_string); } GC::Ref vmin(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vmin"_fly_string); } GC::Ref vmax(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "vmax"_fly_string); } GC::Ref svw(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svw"_fly_string); } GC::Ref svh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svh"_fly_string); } GC::Ref svi(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svi"_fly_string); } GC::Ref svb(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svb"_fly_string); } GC::Ref svmin(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svmin"_fly_string); } GC::Ref svmax(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "svmax"_fly_string); } GC::Ref lvw(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvw"_fly_string); } GC::Ref lvh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvh"_fly_string); } GC::Ref lvi(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvi"_fly_string); } GC::Ref lvb(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvb"_fly_string); } GC::Ref lvmin(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvmin"_fly_string); } GC::Ref lvmax(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "lvmax"_fly_string); } GC::Ref dvw(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvw"_fly_string); } GC::Ref dvh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvh"_fly_string); } GC::Ref dvi(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvi"_fly_string); } GC::Ref dvb(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvb"_fly_string); } GC::Ref dvmin(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvmin"_fly_string); } GC::Ref dvmax(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "dvmax"_fly_string); } GC::Ref cqw(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqw"_fly_string); } GC::Ref cqh(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqh"_fly_string); } GC::Ref cqi(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqi"_fly_string); } GC::Ref cqb(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqb"_fly_string); } GC::Ref cqmin(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqmin"_fly_string); } GC::Ref cqmax(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cqmax"_fly_string); } GC::Ref cm(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "cm"_fly_string); } GC::Ref mm(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "mm"_fly_string); } GC::Ref q(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "q"_fly_string); } GC::Ref in(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "in"_fly_string); } GC::Ref pt(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "pt"_fly_string); } GC::Ref pc(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "pc"_fly_string); } GC::Ref px(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "px"_fly_string); } // GC::Ref deg(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "deg"_fly_string); } GC::Ref grad(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "grad"_fly_string); } GC::Ref rad(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "rad"_fly_string); } GC::Ref turn(JS::VM& vm, WebIDL::Double value) { return numeric_factory(vm, value, "turn"_fly_string); } //