mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 05:39:11 +00:00
LibWeb: Serialize CSS declarations as shorthands where applicable
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macOS, macos-15, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, Linux, blacksmith-16vcpu-ubuntu-2404, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, Linux, blacksmith-16vcpu-ubuntu-2404, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, Linux, blacksmith-16vcpu-ubuntu-2404, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macOS, macOS-universal2, macos-15) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, Linux, Linux-x86_64, blacksmith-8vcpu-ubuntu-2404) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macOS, macos-15, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, Linux, blacksmith-16vcpu-ubuntu-2404, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, Linux, blacksmith-16vcpu-ubuntu-2404, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, Linux, blacksmith-16vcpu-ubuntu-2404, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macOS, macOS-universal2, macos-15) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, Linux, Linux-x86_64, blacksmith-8vcpu-ubuntu-2404) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
When serializing CSS declarations we now support combining multiple properties into a single shorthand property in some cases. This comes with a healthy dose of FIXMEs, including work to be done around supporting: - Nested shorthands (e.g. background, border, etc) - Shorthands which aren't represented by the ShorthandStyleValue type - Subproperties pending substitution This gains us a bunch of new test passes, both for WPT and in-tree
This commit is contained in:
parent
94f5a51820
commit
ed65d5b342
Notes:
github-actions[bot]
2025-05-29 10:05:38 +00:00
Author: https://github.com/Calme1709
Commit: ed65d5b342
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4832
Reviewed-by: https://github.com/AtkinsSJ
Reviewed-by: https://github.com/gmta ✅
9 changed files with 249 additions and 36 deletions
|
@ -1172,22 +1172,96 @@ String CSSStyleProperties::serialized() const
|
|||
if (already_serialized.contains(property))
|
||||
continue;
|
||||
|
||||
// FIXME: 3. If property maps to one or more shorthand properties, let shorthands be an array of those shorthand properties, in preferred order.
|
||||
// 3. If property maps to one or more shorthand properties, let shorthands be an array of those shorthand properties, in preferred order.
|
||||
// FIXME: We don't properly support nested shorthands (e.g. background)
|
||||
if (property_maps_to_shorthand(property)) {
|
||||
// FIXME: Sort in the preferred order. https://www.w3.org/TR/cssom/#concept-shorthands-preferred-order
|
||||
auto shorthands = shorthands_for_longhand(property);
|
||||
|
||||
// FIXME: 4. Shorthand loop: For each shorthand in shorthands, follow these substeps: ...
|
||||
// 4. Shorthand loop: For each shorthand in shorthands, follow these substeps:
|
||||
for (auto shorthand : shorthands) {
|
||||
// 1. Let longhands be an array consisting of all CSS declarations in declaration block’s declarations
|
||||
// that that are not in already serialized and have a property name that maps to one of the shorthand
|
||||
// properties in shorthands.
|
||||
Vector<StyleProperty> longhands;
|
||||
|
||||
// 5. Let value be the result of invoking serialize a CSS value of declaration.
|
||||
auto value = declaration.value->to_string(Web::CSS::SerializationMode::Normal);
|
||||
for (auto const& longhand_declaration : m_properties) {
|
||||
// FIXME: Some of the ad-hoc ShorthandStyleValue::to_string cases don't account for the possibility
|
||||
// of subproperty values pending substitution, to avoid crashing we don't include those here
|
||||
if (!already_serialized.contains(longhand_declaration.property_id) && shorthands_for_longhand(longhand_declaration.property_id).contains_slow(shorthand) && !longhand_declaration.value->is_pending_substitution())
|
||||
longhands.append(longhand_declaration);
|
||||
}
|
||||
|
||||
// 6. Let serialized declaration be the result of invoking serialize a CSS declaration with property name property, value value,
|
||||
// and the important flag set if declaration has its important flag set.
|
||||
auto serialized_declaration = serialize_a_css_declaration(string_from_property_id(property), move(value), declaration.important);
|
||||
// 2. If all properties that map to shorthand are not present in longhands, continue with the steps labeled shorthand loop.
|
||||
if (longhands.is_empty())
|
||||
continue;
|
||||
|
||||
// 7. Append serialized declaration to list.
|
||||
list.append(move(serialized_declaration));
|
||||
// 3. Let current longhands be an empty array.
|
||||
Vector<StyleProperty> current_longhands;
|
||||
|
||||
// 8. Append property to already serialized.
|
||||
already_serialized.set(property);
|
||||
// 4. Append all CSS declarations in longhands that have a property name that maps to shorthand to current longhands.
|
||||
for (auto const& longhand : longhands) {
|
||||
if (shorthands_for_longhand(longhand.property_id).contains_slow(shorthand))
|
||||
current_longhands.append(longhand);
|
||||
}
|
||||
|
||||
// 5. If there is one or more CSS declarations in current longhands have their important flag set and
|
||||
// one or more with it unset, continue with the steps labeled shorthand loop.
|
||||
auto all_declarations_have_same_important_flag = true;
|
||||
|
||||
for (size_t i = 1; i < current_longhands.size(); ++i) {
|
||||
if (current_longhands[i].important != current_longhands[0].important) {
|
||||
all_declarations_have_same_important_flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_declarations_have_same_important_flag)
|
||||
continue;
|
||||
|
||||
// FIXME: 6. If there’s any declaration in declaration block in between the first and the last longhand
|
||||
// in current longhands which belongs to the same logical property group, but has a different
|
||||
// mapping logic as any of the longhands in current longhands, and is not in current
|
||||
// longhands, continue with the steps labeled shorthand loop.
|
||||
|
||||
// 7. Let value be the result of invoking serialize a CSS value of current longhands.
|
||||
auto value = serialize_a_css_value(current_longhands);
|
||||
|
||||
// 8. If value is the empty string, continue with the steps labeled shorthand loop.
|
||||
if (value.is_empty())
|
||||
continue;
|
||||
|
||||
// 9. Let serialized declaration be the result of invoking serialize a CSS declaration with property
|
||||
// name shorthand, value value, and the important flag set if the CSS declarations in current
|
||||
// longhands have their important flag set.
|
||||
auto serialized_declaration = serialize_a_css_declaration(string_from_property_id(shorthand), move(value), current_longhands.first().important);
|
||||
|
||||
// 10. Append serialized declaration to list.
|
||||
list.append(move(serialized_declaration));
|
||||
|
||||
// 11. Append the property names of all items of current longhands to already serialized.
|
||||
for (auto const& longhand : current_longhands)
|
||||
already_serialized.set(longhand.property_id);
|
||||
|
||||
// 12. Continue with the steps labeled declaration loop.
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: File spec issue that this should only be run if we haven't serialized this declaration in the above shorthand loop.
|
||||
if (!already_serialized.contains(declaration.property_id)) {
|
||||
// 5. Let value be the result of invoking serialize a CSS value of declaration.
|
||||
auto value = serialize_a_css_value(declaration);
|
||||
|
||||
// 6. Let serialized declaration be the result of invoking serialize a CSS declaration with property name property, value value,
|
||||
// and the important flag set if declaration has its important flag set.
|
||||
auto serialized_declaration = serialize_a_css_declaration(string_from_property_id(property), move(value), declaration.important);
|
||||
|
||||
// 7. Append serialized declaration to list.
|
||||
list.append(move(serialized_declaration));
|
||||
|
||||
// 8. Append property to already serialized.
|
||||
already_serialized.set(property);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return list joined with " " (U+0020).
|
||||
|
@ -1196,6 +1270,71 @@ String CSSStyleProperties::serialized() const
|
|||
return MUST(builder.to_string());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom/#serialize-a-css-value
|
||||
String CSSStyleProperties::serialize_a_css_value(StyleProperty const& declaration) const
|
||||
{
|
||||
// 1. If If this algorithm is invoked with a list list:
|
||||
// NOTE: This is handled in other other overload of this method
|
||||
|
||||
// 2. Represent the value of the declaration as a list of CSS component values components that, when parsed
|
||||
// according to the property’s grammar, would represent that value. Additionally:
|
||||
// - If certain component values can appear in any order without changing the meaning of the value (a pattern
|
||||
// typically represented by a double bar || in the value syntax), reorder the component values to use the
|
||||
// canonical order of component values as given in the property definition table.
|
||||
// - If component values can be omitted or replaced with a shorter representation without changing the meaning
|
||||
// of the value, omit/replace them.
|
||||
// - If either of the above syntactic translations would be less backwards-compatible, do not perform them.
|
||||
|
||||
// Spec Note: The rules described here outlines the general principles of serialization. For legacy reasons, some
|
||||
// properties serialize in a different manner, which is intentionally undefined here due to lack of
|
||||
// resources. Please consult your local reverse-engineer for details.
|
||||
|
||||
// 3. Remove any <whitespace-token>s from components.
|
||||
// 4. Replace each component value in components with the result of invoking serialize a CSS component value.
|
||||
// 5. Join the items of components into a single string, inserting " " (U+0020 SPACE) between each pair of items
|
||||
// unless the second item is a "," (U+002C COMMA) Return the result.
|
||||
|
||||
// AD-HOC: As the spec is vague we don't follow it exactly here.
|
||||
return declaration.value->to_string(Web::CSS::SerializationMode::Normal);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom/#serialize-a-css-value
|
||||
String CSSStyleProperties::serialize_a_css_value(Vector<StyleProperty> list) const
|
||||
{
|
||||
if (list.is_empty())
|
||||
return String {};
|
||||
|
||||
// 1. Let shorthand be the first shorthand property, in preferred order, that exactly maps to all of the longhand properties in list.
|
||||
// FIXME: Sort in the preferred order. https://www.w3.org/TR/cssom/#concept-shorthands-preferred-order
|
||||
Optional<PropertyID> shorthand = shorthands_for_longhand(list.first().property_id).first_matching([&](PropertyID shorthand) {
|
||||
auto longhands_for_potential_shorthand = longhands_for_shorthand(shorthand);
|
||||
|
||||
// The potential shorthand exactly maps to all of the longhand properties in list if:
|
||||
// a. The number of longhand properties in the list is equal to the number of longhand properties that the potential shorthand maps to.
|
||||
if (longhands_for_potential_shorthand.size() != list.size())
|
||||
return false;
|
||||
|
||||
// b. All longhand properties in the list are contained in the list of longhands for the potential shorthand.
|
||||
return all_of(longhands_for_potential_shorthand, [&](auto longhand) { return any_of(list, [&](auto const& declaration) { return declaration.property_id == longhand; }); });
|
||||
});
|
||||
|
||||
// 2. If there is no such shorthand or shorthand cannot exactly represent the values of all the properties in list, return the empty string.
|
||||
if (!shorthand.has_value())
|
||||
return String {};
|
||||
|
||||
// 3. Otherwise, serialize a CSS value from a hypothetical declaration of the property shorthand with its value representing the combined values of the declarations in list.
|
||||
// FIXME: Not all shorthands are represented by ShorthandStyleValue, we still need to add support for those that don't.
|
||||
Vector<PropertyID> longhand_ids;
|
||||
Vector<ValueComparingNonnullRefPtr<CSSStyleValue const>> longhand_values;
|
||||
|
||||
for (auto const& longhand : list) {
|
||||
longhand_ids.append(longhand.property_id);
|
||||
longhand_values.append(longhand.value);
|
||||
}
|
||||
|
||||
return ShorthandStyleValue::create(shorthand.value(), longhand_ids, longhand_values)->to_string(SerializationMode::Normal);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
|
||||
WebIDL::ExceptionOr<void> CSSStyleProperties::set_css_text(StringView css_text)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
WebIDL::ExceptionOr<void> set_css_float(StringView);
|
||||
|
||||
virtual String serialized() const final override;
|
||||
String serialize_a_css_value(StyleProperty const&) const;
|
||||
String serialize_a_css_value(Vector<StyleProperty>) const;
|
||||
virtual WebIDL::ExceptionOr<void> set_css_text(StringView) override;
|
||||
|
||||
void set_declarations_from_text(StringView);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue