mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-04 00:27:47 +00:00
LibWeb/CSS: Reify UnresolvedStyleValue as CSSUnparsedValue
This commit is contained in:
parent
6428c9990d
commit
0fc512e56d
Notes:
github-actions[bot]
2025-08-21 09:23:05 +00:00
Author: https://github.com/AtkinsSJ
Commit: 0fc512e56d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5901
5 changed files with 132 additions and 9 deletions
|
@ -8,6 +8,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <LibWeb/CSS/CSSUnparsedValue.h>
|
||||||
|
#include <LibWeb/CSS/CSSVariableReferenceValue.h>
|
||||||
|
#include <LibWeb/CSS/Parser/ArbitrarySubstitutionFunctions.h>
|
||||||
|
#include <LibWeb/CSS/PropertyName.h>
|
||||||
#include <LibWeb/CSS/Serialize.h>
|
#include <LibWeb/CSS/Serialize.h>
|
||||||
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
|
||||||
|
|
||||||
|
@ -51,4 +55,120 @@ bool UnresolvedStyleValue::equals(StyleValue const& other) const
|
||||||
return values() == other.as_unresolved().values();
|
return values() == other.as_unresolved().values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GC::Ref<CSSUnparsedValue> reify_a_list_of_component_values(JS::Realm&, Vector<Parser::ComponentValue>);
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-typed-om-1/#reify-var
|
||||||
|
static GC::Root<CSSVariableReferenceValue> reify_a_var_reference(JS::Realm& realm, Parser::Function function)
|
||||||
|
{
|
||||||
|
// NB: A var() might not be representable as a CSSVariableReferenceValue, for example if it has invalid syntax or
|
||||||
|
// it contains an ASF in its variable-name slot. In those cases, we return null here, so it's treated like a
|
||||||
|
// regular function.
|
||||||
|
auto maybe_var_arguments = Parser::parse_according_to_argument_grammar(Parser::ArbitrarySubstitutionFunction::Var, function.value);
|
||||||
|
if (!maybe_var_arguments.has_value())
|
||||||
|
return nullptr;
|
||||||
|
auto var_arguments = maybe_var_arguments.release_value();
|
||||||
|
// NB: Try to parse the variable name. If we can't, return null as above.
|
||||||
|
|
||||||
|
Parser::TokenStream tokens { var_arguments.first() };
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
auto& maybe_variable = tokens.consume_a_token();
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
if (tokens.has_next_token()
|
||||||
|
|| !maybe_variable.is(Parser::Token::Type::Ident)
|
||||||
|
|| !is_a_custom_property_name_string(maybe_variable.token().ident()))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// To reify a var() reference var:
|
||||||
|
// 1. Let object be a new CSSVariableReferenceValue.
|
||||||
|
|
||||||
|
// 2. Set object’s variable internal slot to the serialization of the <custom-ident> providing the variable name.
|
||||||
|
FlyString variable = maybe_variable.token().ident();
|
||||||
|
|
||||||
|
// 3. If var has a fallback value, set object’s fallback internal slot to the result of reifying the fallback’s
|
||||||
|
// component values. Otherwise, set it to null.
|
||||||
|
GC::Ptr<CSSUnparsedValue> fallback;
|
||||||
|
if (var_arguments.size() > 1)
|
||||||
|
fallback = reify_a_list_of_component_values(realm, var_arguments[1]);
|
||||||
|
|
||||||
|
// 4. Return object.
|
||||||
|
return CSSVariableReferenceValue::create(realm, move(variable), move(fallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Reifier {
|
||||||
|
public:
|
||||||
|
static Vector<GCRootCSSUnparsedSegment> reify(JS::Realm& realm, Vector<Parser::ComponentValue> const& source_values)
|
||||||
|
{
|
||||||
|
Reifier reifier;
|
||||||
|
reifier.process_values(realm, source_values);
|
||||||
|
if (!reifier.m_unserialized_values.is_empty())
|
||||||
|
reifier.serialize_unserialized_values();
|
||||||
|
return move(reifier.m_reified_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void process_values(JS::Realm& realm, Vector<Parser::ComponentValue> const& source_values)
|
||||||
|
{
|
||||||
|
// NB: var() could be arbitrarily nested within other functions and blocks, so we have to walk the tree.
|
||||||
|
// Also, a var() might not be representable, if it has an ASF in place of its name, so those will be part
|
||||||
|
// of a string instead.
|
||||||
|
for (auto const& component_value : source_values) {
|
||||||
|
if (component_value.is_function("var"sv)) {
|
||||||
|
// First parse the var() to see if it is representable as a CSSVariableReferenceValue. It might not be,
|
||||||
|
// for example if it has an ASF in the place of its variable name. In that case we fall back to
|
||||||
|
// serializing it like a regular function.
|
||||||
|
if (auto var_reference = reify_a_var_reference(realm, component_value.function())) {
|
||||||
|
serialize_unserialized_values();
|
||||||
|
m_reified_values.append(move(var_reference));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component_value.is_function()) {
|
||||||
|
auto& function = component_value.function();
|
||||||
|
m_unserialized_values.append(function.name_token);
|
||||||
|
process_values(realm, function.value);
|
||||||
|
m_unserialized_values.append(function.end_token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component_value.is_block()) {
|
||||||
|
auto& block = component_value.block();
|
||||||
|
m_unserialized_values.append(block.token);
|
||||||
|
process_values(realm, block.value);
|
||||||
|
m_unserialized_values.append(block.end_token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unserialized_values.append(component_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialize_unserialized_values()
|
||||||
|
{
|
||||||
|
// FIXME: Stop inserting whitespace once we stop removing it during parsing.
|
||||||
|
m_reified_values.append(serialize_a_series_of_component_values(m_unserialized_values, InsertWhitespace::Yes));
|
||||||
|
m_unserialized_values.clear_with_capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<GCRootCSSUnparsedSegment> m_reified_values {};
|
||||||
|
Vector<Parser::ComponentValue> m_unserialized_values {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static GC::Ref<CSSUnparsedValue> reify_a_list_of_component_values(JS::Realm& realm, Vector<Parser::ComponentValue> component_values)
|
||||||
|
{
|
||||||
|
// To reify a list of component values from a list:
|
||||||
|
// 1. Replace all var() references in list with CSSVariableReferenceValue objects, as described in §5.4 var() References.
|
||||||
|
// 2. Replace each remaining maximal subsequence of component values in list with a single string of their concatenated serializations.
|
||||||
|
auto reified_values = Reifier::reify(realm, component_values);
|
||||||
|
|
||||||
|
// 3. Return a new CSSUnparsedValue whose [[tokens]] slot is set to list.
|
||||||
|
return CSSUnparsedValue::create(realm, move(reified_values));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-typed-om-1/#reify-a-list-of-component-values
|
||||||
|
GC::Ref<CSSStyleValue> UnresolvedStyleValue::reify(JS::Realm& realm, String const&) const
|
||||||
|
{
|
||||||
|
return reify_a_list_of_component_values(realm, m_values);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ public:
|
||||||
|
|
||||||
virtual bool equals(StyleValue const& other) const override;
|
virtual bool equals(StyleValue const& other) const override;
|
||||||
|
|
||||||
|
virtual GC::Ref<CSSStyleValue> reify(JS::Realm&, String const& associated_property) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UnresolvedStyleValue(Vector<Parser::ComponentValue>&& values, Parser::SubstitutionFunctionsPresence, Optional<String> original_source_text);
|
UnresolvedStyleValue(Vector<Parser::ComponentValue>&& values, Parser::SubstitutionFunctionsPresence, Optional<String> original_source_text);
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,12 @@ Harness status: OK
|
||||||
|
|
||||||
Found 7 tests
|
Found 7 tests
|
||||||
|
|
||||||
7 Fail
|
2 Pass
|
||||||
|
5 Fail
|
||||||
Fail Computed StylePropertyMap contains every CSS property
|
Fail Computed StylePropertyMap contains every CSS property
|
||||||
Fail Computed StylePropertyMap contains CSS property declarations in style rules
|
Fail Computed StylePropertyMap contains CSS property declarations in style rules
|
||||||
Fail Computed StylePropertyMap contains custom property declarations in style rules
|
Pass Computed StylePropertyMap contains custom property declarations in style rules
|
||||||
Fail Computed StylePropertyMap contains CSS property declarations in inline styles
|
Fail Computed StylePropertyMap contains CSS property declarations in inline styles
|
||||||
Fail Computed StylePropertyMap contains custom property declarations in inline rules
|
Pass Computed StylePropertyMap contains custom property declarations in inline rules
|
||||||
Fail Computed StylePropertyMap contains computed values and not resolved values
|
Fail Computed StylePropertyMap contains computed values and not resolved values
|
||||||
Fail Computed StylePropertyMap is live
|
Fail Computed StylePropertyMap is live
|
|
@ -2,11 +2,11 @@ Harness status: OK
|
||||||
|
|
||||||
Found 6 tests
|
Found 6 tests
|
||||||
|
|
||||||
1 Pass
|
2 Pass
|
||||||
5 Fail
|
4 Fail
|
||||||
Pass Getting a custom property not in the computed style returns undefined
|
Pass Getting a custom property not in the computed style returns undefined
|
||||||
Fail Getting a valid property from computed style returns the correct entry
|
Fail Getting a valid property from computed style returns the correct entry
|
||||||
Fail Getting a valid custom property from computed style returns the correct entry
|
Pass Getting a valid custom property from computed style returns the correct entry
|
||||||
Fail Getting a list-valued property from computed style returns only the first value
|
Fail Getting a list-valued property from computed style returns only the first value
|
||||||
Fail Computed StylePropertyMap.get is not case-sensitive
|
Fail Computed StylePropertyMap.get is not case-sensitive
|
||||||
Fail Computed StylePropertyMap.get reflects updates in inline style
|
Fail Computed StylePropertyMap.get reflects updates in inline style
|
|
@ -2,11 +2,11 @@ Harness status: OK
|
||||||
|
|
||||||
Found 6 tests
|
Found 6 tests
|
||||||
|
|
||||||
2 Pass
|
3 Pass
|
||||||
4 Fail
|
3 Fail
|
||||||
Pass Calling StylePropertyMap.getAll with an unsupported property throws a TypeError
|
Pass Calling StylePropertyMap.getAll with an unsupported property throws a TypeError
|
||||||
Pass Calling StylePropertyMap.getAll with a custom property not in the property model returns an empty list
|
Pass Calling StylePropertyMap.getAll with a custom property not in the property model returns an empty list
|
||||||
Fail Calling StylePropertyMap.getAll with a valid property returns a single element list with the correct entry
|
Fail Calling StylePropertyMap.getAll with a valid property returns a single element list with the correct entry
|
||||||
Fail StylePropertyMap.getAll is case-insensitive
|
Fail StylePropertyMap.getAll is case-insensitive
|
||||||
Fail Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry
|
Pass Calling StylePropertyMap.getAll with a valid custom property returns a single element list with the correct entry
|
||||||
Fail Calling StylePropertyMap.getAll with a list-valued property returns all the values
|
Fail Calling StylePropertyMap.getAll with a list-valued property returns all the values
|
Loading…
Add table
Add a link
Reference in a new issue