ladybird/Libraries/LibWeb/CSS/CSSFunctionRule.cpp
Callum Law 19c8eb4146 LibWeb: Implement CSSFunctionRule
Parsing/using this rule will come in later commits
2026-03-27 11:19:28 +00:00

190 lines
7.2 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2026, Callum Law <callumlaw1709@outlook.com
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSFunctionRule.h"
#include <LibWeb/Bindings/CSSFunctionRulePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/Serialize.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSFunctionRule);
// https://drafts.csswg.org/css-mixins-1/#dictdef-functionparameter
FunctionParameter FunctionParameter::from_internal_function_parameter(FunctionParameterInternal const& internal)
{
return {
// name
// The name of the function parameter.
internal.name,
// type
// The type of the function parameter, represented as a syntax string, or "*" if the parameter has no type.
internal.type->to_string(),
// defaultValue
// The default value of the function parameter, or `null` if the argument does not have a default.
internal.default_value.has_value() ? MUST(serialize_a_series_of_component_values(internal.default_value.value()).trim_ascii_whitespace()) : Optional<String> {},
};
}
// https://drafts.csswg.org/css-mixins-1/#serialize-a-css-type
static void serialize_a_css_type(StringBuilder& builder, Parser::SyntaxNode const& type)
{
// To serialize a CSS type, return the concatenation of the following:
// If the <css-type> consists of a single <syntax-component>, return the corresponding syntax string.
auto const is_single_syntax_component = [&]() {
if (type.type() == Parser::SyntaxNode::NodeType::Universal)
return true;
if (first_is_one_of(type.type(), Parser::SyntaxNode::NodeType::Ident, Parser::SyntaxNode::NodeType::Type))
return true;
if (first_is_one_of(type.type(), Parser::SyntaxNode::NodeType::Multiplier, Parser::SyntaxNode::NodeType::CommaSeparatedMultiplier) && first_is_one_of(as<Parser::MultiplierSyntaxNode>(type).child().type(), Parser::SyntaxNode::NodeType::Ident, Parser::SyntaxNode::NodeType::Type))
return true;
return false;
}();
if (is_single_syntax_component) {
builder.append(type.to_string());
return;
}
// Otherwise, return the concatenation of the following:
// The string "type(", i.e. "type" followed by a single LEFT PARENTHESIS (U+0028).
builder.append("type("sv);
// The corresponding syntax string.
builder.append(type.to_string());
// The string ")", i.e. a single RIGHT PARENTHESIS (U+0029).
builder.append(')');
}
// https://drafts.csswg.org/css-mixins-1/#serialize-a-function-parameter
void FunctionParameterInternal::serialize(StringBuilder& builder) const
{
// To serialize a function parameter, return the concatenation of the following:
// The result of performing serialize an identifier on the name of the function parameter.
serialize_an_identifier(builder, name);
// If the function parameter has a type, and that type is not the universal syntax definition:
if (type->type() != Parser::SyntaxNode::NodeType::Universal) {
// - A single SPACE (U+0020), followed by the result of performing serialize a CSS type on that type.
builder.append(' ');
serialize_a_css_type(builder, *type);
}
// If the function parameter has a default value:
if (default_value.has_value()) {
// - A single COLON (U+003A), followed by a single SPACE (U+0020), followed by the result of performing
// serialize a CSS value on that value.
builder.appendff(": {}", MUST(serialize_a_series_of_component_values(default_value.value()).trim_ascii_whitespace()));
}
}
GC::Ref<CSSFunctionRule> CSSFunctionRule::create(JS::Realm& realm, CSSRuleList& rules, FlyString name, Vector<FunctionParameterInternal> parameters, NonnullOwnPtr<Parser::SyntaxNode> return_type)
{
return realm.create<CSSFunctionRule>(realm, rules, move(name), move(parameters), move(return_type));
}
CSSFunctionRule::CSSFunctionRule(JS::Realm& realm, CSSRuleList& rules, FlyString name, Vector<FunctionParameterInternal> parameters, NonnullOwnPtr<Parser::SyntaxNode> return_type)
: CSSGroupingRule(realm, rules, Type::Function)
, m_name(move(name))
, m_parameters(move(parameters))
, m_return_type(move(return_type))
{
}
void CSSFunctionRule::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSFunctionRule);
Base::initialize(realm);
}
// https://drafts.csswg.org/css-mixins-1/#dom-cssfunctionrule-getparameters
Vector<FunctionParameter> CSSFunctionRule::get_parameters() const
{
Vector<FunctionParameter> parameters;
parameters.ensure_capacity(m_parameters.size());
for (auto const& parameter : m_parameters)
parameters.append(FunctionParameter::from_internal_function_parameter(parameter));
return parameters;
}
// https://drafts.csswg.org/css-mixins-1/#dom-cssfunctionrule-returntype
String CSSFunctionRule::return_type() const
{
// The return type of the custom function, represented as a syntax string. If the custom function has no return
// type, returns "*".
// NB: We always store a return type (defaulting to "*")
return m_return_type->to_string();
}
// https://drafts.csswg.org/css-mixins-1/#serialize-a-cssfunctionrule
String CSSFunctionRule::serialized() const
{
// To serialize a CSSFunctionRule, return the concatenation of the following:
StringBuilder builder;
// 1. The string "@function" followed by a single SPACE (U+0020).
builder.append("@function "sv);
// 2. The result of performing serialize an identifier on the name of the custom function, followed by a single LEFT
// PARENTHESIS (U+0028).
serialize_an_identifier(builder, m_name);
builder.append('(');
// 3. The result of serialize a function parameter on each of the custom functions parameters, all joined by ", "
// (COMMA U+002C, followed by a single SPACE U+0020).
for (size_t i = 0; i < m_parameters.size(); ++i) {
if (i > 0)
builder.append(", "sv);
m_parameters[i].serialize(builder);
}
// 4. A single RIGHT PARENTHESIS (U+0029).
builder.append(')');
// 5. If the custom function has return type, and that return type is not the universal syntax definition ("*"):
if (m_return_type->type() != Parser::SyntaxNode::NodeType::Universal) {
// - A single SPACE (U+0020), followed by the string "returns", followed by a single SPACE (U+0020).
builder.append(" returns "sv);
// - The result of performing serialize a CSS type on that type.
serialize_a_css_type(builder, *m_return_type);
}
// 6. A single SPACE (U+0020), followed by a LEFT CURLY BRACKET (U+007B).
builder.append(" {"sv);
// 7. The result of performing serialize a CSS rule on each rule in cssRules, filtering out empty strings, each
// preceded by a single SPACE (U+0020).
auto const& rules = css_rules();
for (size_t i = 0; i < rules.length(); ++i) {
auto const& rule = rules.item(i);
auto serialized_rule = rule->serialized();
if (!serialized_rule.is_empty()) {
builder.append(' ');
builder.append(serialized_rule);
}
}
// 8. A single SPACE (U+0020), followed by a single RIGHT CURLY BRACKET (U+007D).
builder.append(" }"sv);
return MUST(builder.to_string());
}
}