Files
ladybird/Libraries/LibWeb/CSS/Parser/ComponentValue.cpp
Andreas Kling 61d79a1e47 LibWeb: Parse CSS image-set()
Add an abstract image style value for image-set() and parse both the
standard and -webkit-prefixed spellings through the existing <image>
value path. The parser accepts URL and string image candidates,
optional resolution descriptors, and type() filters.

Track attr-taint through substituted component values so image-set()
candidates using attr()-derived URL-producing tokens are rejected when
resolved for URL-using properties.

Update the relevant WPT baselines now that image-set() parsing is
supported in additional value contexts.
2026-04-25 14:54:10 +02:00

134 lines
3.6 KiB
C++

/*
* Copyright (c) 2020-2021, the SerenityOS developers.
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CSS/Parser/ComponentValue.h>
namespace Web::CSS::Parser {
ComponentValue::ComponentValue(Token token)
: m_value(move(token))
{
}
ComponentValue::ComponentValue(Function&& function)
: m_value(move(function))
{
}
ComponentValue::ComponentValue(SimpleBlock&& block)
: m_value(move(block))
{
}
ComponentValue::ComponentValue(GuaranteedInvalidValue&& invalid)
: m_value(move(invalid))
{
}
ComponentValue::~ComponentValue() = default;
bool ComponentValue::is_function(StringView name) const
{
return is_function() && function().name.equals_ignoring_ascii_case(name);
}
bool ComponentValue::is_delim(u32 delim) const
{
switch (delim) {
// All of these have their own separate token types, and so passing them to is_delim() is incorrect and will never match.
case ':':
case ';':
case ',':
case '[':
case ']':
case '{':
case '}':
case '(':
case ')':
dbgln("Calling is_delim() with a punctuation mark ({}) that has its own token type!", static_cast<char>(delim));
VERIFY_NOT_REACHED();
default:
return is(Token::Type::Delim) && token().delim() == delim;
}
}
bool ComponentValue::is_ident(StringView ident) const
{
return is(Token::Type::Ident) && token().ident().equals_ignoring_ascii_case(ident);
}
String ComponentValue::to_string() const
{
return m_value.visit([](auto const& it) { return it.to_string(); });
}
String ComponentValue::to_debug_string() const
{
return m_value.visit(
[](Token const& token) {
return MUST(String::formatted("Token: {}", token.to_debug_string()));
},
[](SimpleBlock const& block) {
return MUST(String::formatted("Block: {}", block.to_string()));
},
[](Function const& function) {
return MUST(String::formatted("Function: {}", function.to_string()));
},
[](GuaranteedInvalidValue const&) {
return "Guaranteed-invalid value"_string;
});
}
String ComponentValue::original_source_text() const
{
return m_value.visit([](auto const& it) { return it.original_source_text(); });
}
bool ComponentValue::contains_guaranteed_invalid_value() const
{
return m_value.visit(
[](Token const&) {
return false;
},
[](SimpleBlock const& block) {
return block.value
.first_matching([](auto const& it) { return it.contains_guaranteed_invalid_value(); })
.has_value();
},
[](Function const& function) {
return function.value
.first_matching([](auto const& it) { return it.contains_guaranteed_invalid_value(); })
.has_value();
},
[](GuaranteedInvalidValue const&) {
return true;
});
}
bool ComponentValue::contains_attr_tainted_value() const
{
if (m_attr_tainted)
return true;
return m_value.visit(
[](Token const&) {
return false;
},
[](SimpleBlock const& block) {
return block.value
.first_matching([](auto const& it) { return it.contains_attr_tainted_value(); })
.has_value();
},
[](Function const& function) {
return function.value
.first_matching([](auto const& it) { return it.contains_attr_tainted_value(); })
.has_value();
},
[](GuaranteedInvalidValue const&) {
return false;
});
}
}