From 056205aa76437eb797f74ce01305d3fd473e0a58 Mon Sep 17 00:00:00 2001 From: Tommy van der Vorst Date: Sat, 1 Mar 2025 11:27:55 +0100 Subject: [PATCH] LibWeb/CSS: Treat 'mask' as a longhand property Before this change, an element masked with 'mask-image: url(...)' would show the mask, but 'mask: url(...)' would not. On e.g. dialogic.nl it would show white boxes instead of the actual images in the top navigation bar. We still do not support many of the other mask properties, but with this change at least the masks show up in both cases. --- Libraries/LibWeb/CSS/Parser/ValueParsing.cpp | 16 ++- Libraries/LibWeb/CSS/Properties.json | 20 ++- Libraries/LibWeb/Layout/Node.cpp | 8 +- Libraries/LibWeb/SVG/SVGGraphicsElement.cpp | 13 +- .../expected/css-mask-longhand.html | 13 ++ .../Screenshot/input/css-mask-longhand.html | 13 ++ ...upported-properties-and-default-values.txt | 2 +- ...eclaration-has-indexed-property-getter.txt | 127 +++++++++--------- .../css/getComputedStyle-print-all.txt | 1 - 9 files changed, 127 insertions(+), 86 deletions(-) create mode 100644 Tests/LibWeb/Screenshot/expected/css-mask-longhand.html create mode 100644 Tests/LibWeb/Screenshot/input/css-mask-longhand.html diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 562dfde9378..599220220a4 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1925,8 +1926,19 @@ RefPtr Parser::parse_string_value(TokenStream& RefPtr Parser::parse_image_value(TokenStream& tokens) { - if (auto url = parse_url_function(tokens); url.has_value()) - return ImageStyleValue::create(url.value()); + tokens.mark(); + auto url = parse_url_function(tokens); + if (url.has_value()) { + // If the value is a 'url(..)' parse as image, but if it is just a reference 'url(#xx)', leave it alone, + // so we can parse as URL further on. These URLs are used as references inside SVG documents for masks. + if (!url.value().equals(m_url, URL::ExcludeFragment::Yes)) { + tokens.discard_a_mark(); + return ImageStyleValue::create(url.value()); + } + tokens.restore_a_mark(); + return nullptr; + } + tokens.discard_a_mark(); if (auto linear_gradient = parse_linear_gradient_function(tokens)) return linear_gradient; diff --git a/Libraries/LibWeb/CSS/Properties.json b/Libraries/LibWeb/CSS/Properties.json index 7c1a8a8b3cf..2f167d8879d 100644 --- a/Libraries/LibWeb/CSS/Properties.json +++ b/Libraries/LibWeb/CSS/Properties.json @@ -1958,25 +1958,21 @@ ] }, "mask": { - "animation-type": "none", - "affects-layout": false, - "affects-stacking-context": true, "inherited": false, - "valid-identifiers": [ - "none" - ], - "__comment": "FIXME: This should be a and/or #", - "valid-types": [ - "url" - ], - "initial": "none" + "__comment": "FIXME: add longhands mask-clip, mask-composite, mask-mode, mask-origin, mask-position, mask-repeat, mask-size; also reset mask-border", + "initial": "none", + "longhands": [ + "mask-image" + ] }, "mask-image": { "animation-type": "discrete", "inherited": false, "affects-layout": false, + "affects-stacking-context": true, "valid-types": [ - "image" + "image", + "url" ], "valid-identifiers": [ "none" diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index b37e2f14c92..aece5f6e4a5 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -827,7 +827,10 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) else if (stroke_width.is_percentage()) computed_values.set_stroke_width(CSS::LengthPercentage { stroke_width.as_percentage().percentage() }); - if (auto const& mask_image = computed_style.property(CSS::PropertyID::MaskImage); mask_image.is_abstract_image()) { + auto const& mask_image = computed_style.property(CSS::PropertyID::MaskImage); + if (mask_image.is_url()) { + computed_values.set_mask(mask_image.as_url().url()); + } else if (mask_image.is_abstract_image()) { auto const& abstract_image = mask_image.as_abstract_image(); computed_values.set_mask_image(abstract_image); const_cast(abstract_image).load_any_resources(document()); @@ -835,9 +838,6 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) computed_values.set_mask_type(computed_style.mask_type()); - if (auto const& mask = computed_style.property(CSS::PropertyID::Mask); mask.is_url()) - computed_values.set_mask(mask.as_url().url()); - auto const& clip_path = computed_style.property(CSS::PropertyID::ClipPath); if (clip_path.is_url()) computed_values.set_clip_path(clip_path.as_url().url()); diff --git a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index 8d9e5d27382..a814961fba7 100644 --- a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -182,8 +182,17 @@ void SVGGraphicsElement::apply_presentational_hints(GC::Refset_property_from_presentational_hint(property.id, style_value.release_nonnull()); + if (property.id == CSS::PropertyID::Mask) { + // Mask is a shorthand property in CSS, but parse_css_value does not take that into account. For now, + // just parse as 'mask-image' as anything else is currently not supported. + // FIXME: properly parse longhand 'mask' property + if (auto style_value = parse_css_value(parsing_context, value, CSS::PropertyID::MaskImage)) { + cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::MaskImage, style_value.release_nonnull()); + } + } else { + if (auto style_value = parse_css_value(parsing_context, value, property.id)) + cascaded_properties->set_property_from_presentational_hint(property.id, style_value.release_nonnull()); + } break; } }); diff --git a/Tests/LibWeb/Screenshot/expected/css-mask-longhand.html b/Tests/LibWeb/Screenshot/expected/css-mask-longhand.html new file mode 100644 index 00000000000..428df7c0d9e --- /dev/null +++ b/Tests/LibWeb/Screenshot/expected/css-mask-longhand.html @@ -0,0 +1,13 @@ + + +
+ + diff --git a/Tests/LibWeb/Screenshot/input/css-mask-longhand.html b/Tests/LibWeb/Screenshot/input/css-mask-longhand.html new file mode 100644 index 00000000000..67ac2854b25 --- /dev/null +++ b/Tests/LibWeb/Screenshot/input/css-mask-longhand.html @@ -0,0 +1,13 @@ + + +
+ + diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt index b3eaf490912..afc43a69d0f 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt @@ -1,6 +1,6 @@ All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle: 'cssText': '' -'length': '220' +'length': '219' 'parentRule': 'null' 'cssFloat': 'none' 'WebkitAlignContent': 'normal' diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt index 15df8d37e4e..aeba2e39f4d 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt @@ -156,70 +156,69 @@ All properties associated with getComputedStyle(document.body): "153": "margin-left", "154": "margin-right", "155": "margin-top", - "156": "mask", - "157": "mask-image", - "158": "mask-type", - "159": "max-block-size", - "160": "max-height", - "161": "max-inline-size", - "162": "max-width", - "163": "min-block-size", - "164": "min-height", - "165": "min-inline-size", - "166": "min-width", - "167": "mix-blend-mode", - "168": "object-fit", - "169": "object-position", - "170": "opacity", - "171": "order", - "172": "outline-color", - "173": "outline-offset", - "174": "outline-style", - "175": "outline-width", - "176": "overflow-x", - "177": "overflow-y", - "178": "padding-block-end", - "179": "padding-block-start", - "180": "padding-bottom", - "181": "padding-inline-end", - "182": "padding-inline-start", - "183": "padding-left", - "184": "padding-right", - "185": "padding-top", - "186": "position", - "187": "r", - "188": "right", - "189": "rotate", - "190": "row-gap", - "191": "rx", - "192": "ry", - "193": "scale", - "194": "scrollbar-gutter", - "195": "scrollbar-width", - "196": "stop-color", - "197": "stop-opacity", - "198": "table-layout", - "199": "text-decoration-color", - "200": "text-decoration-style", - "201": "text-decoration-thickness", - "202": "text-overflow", - "203": "top", - "204": "transform", - "205": "transform-box", - "206": "transform-origin", - "207": "transition-delay", - "208": "transition-duration", - "209": "transition-property", - "210": "transition-timing-function", - "211": "translate", - "212": "unicode-bidi", - "213": "user-select", - "214": "vertical-align", - "215": "view-transition-name", - "216": "width", - "217": "x", - "218": "y", - "219": "z-index" + "156": "mask-image", + "157": "mask-type", + "158": "max-block-size", + "159": "max-height", + "160": "max-inline-size", + "161": "max-width", + "162": "min-block-size", + "163": "min-height", + "164": "min-inline-size", + "165": "min-width", + "166": "mix-blend-mode", + "167": "object-fit", + "168": "object-position", + "169": "opacity", + "170": "order", + "171": "outline-color", + "172": "outline-offset", + "173": "outline-style", + "174": "outline-width", + "175": "overflow-x", + "176": "overflow-y", + "177": "padding-block-end", + "178": "padding-block-start", + "179": "padding-bottom", + "180": "padding-inline-end", + "181": "padding-inline-start", + "182": "padding-left", + "183": "padding-right", + "184": "padding-top", + "185": "position", + "186": "r", + "187": "right", + "188": "rotate", + "189": "row-gap", + "190": "rx", + "191": "ry", + "192": "scale", + "193": "scrollbar-gutter", + "194": "scrollbar-width", + "195": "stop-color", + "196": "stop-opacity", + "197": "table-layout", + "198": "text-decoration-color", + "199": "text-decoration-style", + "200": "text-decoration-thickness", + "201": "text-overflow", + "202": "top", + "203": "transform", + "204": "transform-box", + "205": "transform-origin", + "206": "transition-delay", + "207": "transition-duration", + "208": "transition-property", + "209": "transition-timing-function", + "210": "translate", + "211": "unicode-bidi", + "212": "user-select", + "213": "vertical-align", + "214": "view-transition-name", + "215": "width", + "216": "x", + "217": "y", + "218": "z-index" } All properties associated with document.body.style by default: {} diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index 3efc595cdcc..90a96c09ed2 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -154,7 +154,6 @@ margin-inline-start: 8px margin-left: 8px margin-right: 8px margin-top: 8px -mask: none mask-image: none mask-type: luminance max-block-size: none