ladybird/Libraries/LibWeb/CSS/BooleanExpression.cpp
Sam Atkins 88aea2e11d LibWeb: Give BooleanExpression an evaluation context
Different users of BooleanExpression have different requirements for
evaluation:
- `@media` needs a Document
- `@supports` doesn't need anything
- `@container` needs a container Element

To support these without expanding the API, replace the Document*
parameter with a BooleanExpressionEvaluationContext type which contains
these different values.

No behaviour changes.
2026-05-13 11:05:31 +01:00

142 lines
4.4 KiB
C++
Raw 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) 2021-2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringBuilder.h>
#include <LibWeb/CSS/BooleanExpression.h>
namespace Web::CSS {
bool BooleanExpression::evaluate_to_boolean(BooleanExpressionEvaluationContext const& context) const
{
return evaluate(context) == MatchResult::True;
}
void BooleanExpression::indent(StringBuilder& builder, int levels)
{
builder.append_repeated(" "sv, levels);
}
void GeneralEnclosed::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.appendff("GeneralEnclosed: {}\n", to_string());
}
MatchResult BooleanNotExpression::evaluate(BooleanExpressionEvaluationContext const& context) const
{
// https://drafts.csswg.org/css-values-5/#boolean-logic
// `not test` evaluates to true if its contained test is false, false if its true, and unknown if its unknown.
switch (m_child->evaluate(context)) {
case MatchResult::False:
return MatchResult::True;
case MatchResult::True:
return MatchResult::False;
case MatchResult::Unknown:
return MatchResult::Unknown;
}
VERIFY_NOT_REACHED();
}
String BooleanNotExpression::to_string() const
{
return MUST(String::formatted("not {}", m_child->to_string()));
}
void BooleanNotExpression::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.append("NOT:\n"sv);
m_child->dump(builder, indent_levels + 1);
}
MatchResult BooleanExpressionInParens::evaluate(BooleanExpressionEvaluationContext const& context) const
{
return m_child->evaluate(context);
}
String BooleanExpressionInParens::to_string() const
{
return MUST(String::formatted("({})", m_child->to_string()));
}
void BooleanExpressionInParens::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.append("(\n"sv);
m_child->dump(builder, indent_levels + 1);
indent(builder, indent_levels);
builder.append(")\n"sv);
}
MatchResult BooleanAndExpression::evaluate(BooleanExpressionEvaluationContext const& context) const
{
// https://drafts.csswg.org/css-values-5/#boolean-logic
// Multiple tests connected with `and` evaluate to true if all of those tests are true, false if any of them are
// false, and unknown otherwise (i.e. if at least one unknown, but no false).
size_t true_results = 0;
for (auto const& child : m_children) {
auto child_match = child->evaluate(context);
if (child_match == MatchResult::False)
return MatchResult::False;
if (child_match == MatchResult::True)
true_results++;
}
if (true_results == m_children.size())
return MatchResult::True;
return MatchResult::Unknown;
}
String BooleanAndExpression::to_string() const
{
return MUST(String::join(" and "sv, m_children));
}
void BooleanAndExpression::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.append("AND:\n"sv);
for (auto const& child : m_children)
child->dump(builder, indent_levels + 1);
}
MatchResult BooleanOrExpression::evaluate(BooleanExpressionEvaluationContext const& context) const
{
// https://drafts.csswg.org/css-values-5/#boolean-logic
// Multiple tests connected with `or` evaluate to true if any of those tests are true, false if all of them are
// false, and unknown otherwise (i.e. at least one unknown, but no true).
size_t false_results = 0;
for (auto const& child : m_children) {
auto child_match = child->evaluate(context);
if (child_match == MatchResult::True)
return MatchResult::True;
if (child_match == MatchResult::False)
false_results++;
}
if (false_results == m_children.size())
return MatchResult::False;
return MatchResult::Unknown;
}
String BooleanOrExpression::to_string() const
{
return MUST(String::join(" or "sv, m_children));
}
void BooleanOrExpression::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.append("OR:\n"sv);
for (auto const& child : m_children)
child->dump(builder, indent_levels + 1);
}
void ConstantBooleanExpression::dump(StringBuilder& builder, int indent_levels) const
{
indent(builder, indent_levels);
builder.appendff("CONSTANT: {}\n", to_string());
}
}