diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 74aa354d092..57bc6652ff0 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -450,6 +450,7 @@ private: RefPtr parse_font_variant_ligatures_value(TokenStream&); RefPtr parse_font_variant_numeric_value(TokenStream&); RefPtr parse_list_style_value(TokenStream&); + RefPtr parse_mask_value(TokenStream&); RefPtr parse_math_depth_value(TokenStream&); RefPtr parse_opacity_value(PropertyID property_id, TokenStream&); RefPtr parse_overflow_value(TokenStream&); diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index 2a24004a463..7c769312b9e 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -664,6 +664,10 @@ Parser::ParseErrorOr> Parser::parse_css_value if (auto parsed_value = parse_math_depth_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::Mask: + if (auto parsed_value = parse_mask_value(tokens); parsed_value && !tokens.has_next_token()) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::MaskClip: case PropertyID::MaskComposite: case PropertyID::MaskImage: @@ -1154,7 +1158,7 @@ RefPtr Parser::parse_background_value(TokenStream Parser::parse_list_style_value(TokenStream Parser::parse_mask_value(TokenStream& tokens) +{ + // https://drafts.fxtf.org/css-masking-1/#the-mask + // # + // + // = + // || + // [ / ]? || + // || + // || + // [ | no-clip ] || + // || + // + auto transaction = tokens.begin_transaction(); + + auto make_mask_shorthand = [](auto mask_image, auto mask_position, auto mask_size, auto mask_repeat, auto mask_origin, auto mask_clip, auto mask_composite, auto mask_mode) { + return ShorthandStyleValue::create(PropertyID::Mask, + { PropertyID::MaskImage, PropertyID::MaskPosition, PropertyID::MaskSize, PropertyID::MaskRepeat, PropertyID::MaskOrigin, PropertyID::MaskClip, PropertyID::MaskComposite, PropertyID::MaskMode }, + { move(mask_image), move(mask_position), move(mask_size), move(mask_repeat), move(mask_origin), move(mask_clip), move(mask_composite), move(mask_mode) }); + }; + + StyleValueVector mask_images; + StyleValueVector mask_positions; + StyleValueVector mask_sizes; + StyleValueVector mask_repeats; + StyleValueVector mask_origins; + StyleValueVector mask_clips; + StyleValueVector mask_composites; + StyleValueVector mask_modes; + + auto initial_mask_image = property_initial_value(PropertyID::MaskImage); + auto initial_mask_position = property_initial_value(PropertyID::MaskPosition); + auto initial_mask_size = property_initial_value(PropertyID::MaskSize); + auto initial_mask_repeat = property_initial_value(PropertyID::MaskRepeat); + auto initial_mask_origin = property_initial_value(PropertyID::MaskOrigin); + auto initial_mask_clip = property_initial_value(PropertyID::MaskClip); + auto initial_mask_composite = property_initial_value(PropertyID::MaskComposite); + auto initial_mask_mode = property_initial_value(PropertyID::MaskMode); + + // Per-layer values + RefPtr mask_image; + RefPtr mask_position; + RefPtr mask_size; + RefPtr mask_repeat; + RefPtr mask_origin; + RefPtr mask_clip; + RefPtr mask_composite; + RefPtr mask_mode; + + bool has_multiple_layers = false; + // MaskSize is always parsed as part of MaskPosition, so we don't include it here. + Vector remaining_layer_properties { + PropertyID::MaskImage, + PropertyID::MaskPosition, + PropertyID::MaskRepeat, + PropertyID::MaskOrigin, + PropertyID::MaskClip, + PropertyID::MaskComposite, + PropertyID::MaskMode, + }; + + auto mask_layer_is_valid = [&]() -> bool { + return mask_image || mask_position || mask_size || mask_repeat || mask_origin || mask_clip || mask_composite || mask_mode; + }; + + auto complete_mask_layer = [&]() { + mask_images.append(mask_image ? mask_image.release_nonnull() : initial_mask_image); + mask_positions.append(mask_position ? mask_position.release_nonnull() : initial_mask_position); + mask_sizes.append(mask_size ? mask_size.release_nonnull() : initial_mask_size); + mask_repeats.append(mask_repeat ? mask_repeat.release_nonnull() : initial_mask_repeat); + mask_composites.append(mask_composite ? mask_composite.release_nonnull() : initial_mask_composite); + mask_modes.append(mask_mode ? mask_mode.release_nonnull() : initial_mask_mode); + + if (!mask_origin && !mask_clip) { + mask_origin = initial_mask_origin; + mask_clip = initial_mask_clip; + } else if (!mask_clip) { + mask_clip = mask_origin; + } + mask_origins.append(mask_origin.release_nonnull()); + mask_clips.append(mask_clip.release_nonnull()); + + mask_image = nullptr; + mask_position = nullptr; + mask_size = nullptr; + mask_repeat = nullptr; + mask_origin = nullptr; + mask_clip = nullptr; + mask_composite = nullptr; + mask_mode = nullptr; + + remaining_layer_properties.clear_with_capacity(); + remaining_layer_properties.unchecked_append(PropertyID::MaskImage); + remaining_layer_properties.unchecked_append(PropertyID::MaskPosition); + remaining_layer_properties.unchecked_append(PropertyID::MaskRepeat); + remaining_layer_properties.unchecked_append(PropertyID::MaskOrigin); + remaining_layer_properties.unchecked_append(PropertyID::MaskClip); + remaining_layer_properties.unchecked_append(PropertyID::MaskComposite); + remaining_layer_properties.unchecked_append(PropertyID::MaskMode); + }; + + while (tokens.has_next_token()) { + if (tokens.next_token().is(Token::Type::Comma)) { + has_multiple_layers = true; + if (!mask_layer_is_valid()) + return nullptr; + complete_mask_layer(); + tokens.discard_a_token(); + continue; + } + + auto value_and_property = parse_css_value_for_properties(remaining_layer_properties, tokens); + if (!value_and_property.has_value()) + return nullptr; + auto& value = value_and_property->style_value; + auto property = value_and_property->property; + remove_property(remaining_layer_properties, property); + + switch (property) { + // + case PropertyID::MaskImage: + VERIFY(!mask_image); + mask_image = value.release_nonnull(); + continue; + // [ / ]? + case PropertyID::MaskPosition: { + VERIFY(!mask_position); + mask_position = value.release_nonnull(); + + // Attempt to parse `/ ` + auto mask_size_transaction = tokens.begin_transaction(); + auto const& maybe_slash = tokens.consume_a_token(); + if (maybe_slash.is_delim('/')) { + if (auto maybe_mask_size = parse_single_background_size_value(PropertyID::MaskSize, tokens)) { + mask_size_transaction.commit(); + mask_size = maybe_mask_size.release_nonnull(); + continue; + } + return nullptr; + } + continue; + } + // + case PropertyID::MaskRepeat: { + VERIFY(!mask_repeat); + tokens.reconsume_current_input_token(); + if (auto maybe_repeat = parse_single_repeat_style_value(property, tokens)) { + mask_repeat = maybe_repeat.release_nonnull(); + continue; + } + return nullptr; + } + // || [ | no-clip ] + // If one value and the no-clip keyword are present then sets mask-origin and + // no-clip sets mask-clip to that value. + // If one value and no no-clip keyword are present then sets both mask-origin and + // mask-clip to that value. + // If two values are present, then the first sets mask-origin and the second mask-clip. + case PropertyID::MaskOrigin: + case PropertyID::MaskClip: { + VERIFY(value->is_keyword()); + if (value->as_keyword().keyword() == Keyword::NoClip) { + VERIFY(!mask_clip); + mask_clip = value.release_nonnull(); + } else if (!mask_origin) { + mask_origin = value.release_nonnull(); + } else if (!mask_clip) { + mask_clip = value.release_nonnull(); + } else { + VERIFY_NOT_REACHED(); + } + continue; + } + // + case PropertyID::MaskComposite: + VERIFY(!mask_composite); + mask_composite = value.release_nonnull(); + continue; + // + case PropertyID::MaskMode: + VERIFY(!mask_mode); + mask_mode = value.release_nonnull(); + continue; + default: + VERIFY_NOT_REACHED(); + } + + VERIFY_NOT_REACHED(); + } + + if (!mask_layer_is_valid()) + return nullptr; + + // We only need to create StyleValueLists if there are multiple layers. + // Otherwise, we can pass the single StyleValue directly + if (has_multiple_layers) { + complete_mask_layer(); + transaction.commit(); + return make_mask_shorthand( + StyleValueList::create(move(mask_images), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_positions), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_sizes), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_repeats), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_origins), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_clips), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_composites), StyleValueList::Separator::Comma), + StyleValueList::create(move(mask_modes), StyleValueList::Separator::Comma)); + } + + if (!mask_image) + mask_image = initial_mask_image; + if (!mask_position) + mask_position = initial_mask_position; + if (!mask_size) + mask_size = initial_mask_size; + if (!mask_repeat) + mask_repeat = initial_mask_repeat; + if (!mask_origin) + mask_origin = initial_mask_origin; + if (!mask_clip) + mask_clip = mask_origin; // intentionally not initial value + if (!mask_composite) + mask_composite = initial_mask_composite; + if (!mask_mode) + mask_mode = initial_mask_mode; + + transaction.commit(); + return make_mask_shorthand( + mask_image.release_nonnull(), + mask_position.release_nonnull(), + mask_size.release_nonnull(), + mask_repeat.release_nonnull(), + mask_origin.release_nonnull(), + mask_clip.release_nonnull(), + mask_composite.release_nonnull(), + mask_mode.release_nonnull()); +} + RefPtr Parser::parse_math_depth_value(TokenStream& tokens) { // https://w3c.github.io/mathml-core/#propdef-math-depth diff --git a/Libraries/LibWeb/CSS/Properties.json b/Libraries/LibWeb/CSS/Properties.json index 22dd2ff092d..8195edd425b 100644 --- a/Libraries/LibWeb/CSS/Properties.json +++ b/Libraries/LibWeb/CSS/Properties.json @@ -2376,11 +2376,19 @@ ] }, "mask": { + "__comment": "FIXME: reset mask-border", "inherited": false, - "__comment": "FIXME: add longhands mask-clip, mask-composite, mask-mode, mask-origin, mask-position, mask-repeat, mask-size; also reset mask-border", + "affects-layout": false, "initial": "none", "longhands": [ - "mask-image" + "mask-clip", + "mask-composite", + "mask-image", + "mask-mode", + "mask-origin", + "mask-position", + "mask-repeat", + "mask-size" ] }, "mask-clip": { diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt b/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt index 61aae1df5d8..b889bcad21f 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleProperties-all-supported-properties-and-default-values.txt @@ -111,9 +111,9 @@ All supported properties and their default values exposed from CSSStylePropertie 'WebkitJustifyContent': 'normal' 'webkitJustifyContent': 'normal' '-webkit-justify-content': 'normal' -'WebkitMask': 'none' -'webkitMask': 'none' -'-webkit-mask': 'none' +'WebkitMask': 'border-box' +'webkitMask': 'border-box' +'-webkit-mask': 'border-box' 'WebkitMaskImage': 'none' 'webkitMaskImage': 'none' '-webkit-mask-image': 'none' @@ -524,7 +524,7 @@ All supported properties and their default values exposed from CSSStylePropertie 'margin-right': '8px' 'marginTop': '8px' 'margin-top': '8px' -'mask': 'none' +'mask': 'border-box' 'maskClip': 'border-box' 'mask-clip': 'border-box' 'maskComposite': 'add' diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/animations/mask-no-interpolation.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/animations/mask-no-interpolation.txt index e721abf7dee..1183c5805a4 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/animations/mask-no-interpolation.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/animations/mask-no-interpolation.txt @@ -2,7 +2,8 @@ Harness status: OK Found 42 tests -42 Pass +28 Pass +14 Fail Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [url(mask.png)] at (-0.3) should be [none] Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [url(mask.png)] at (0) should be [none] Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [url(mask.png)] at (0.3) should be [none] @@ -31,17 +32,17 @@ Pass CSS Transitions with transition: all: property from [none] to [url(m Pass CSS Transitions with transition: all: property from [none] to [url(mask.png)] at (0.6) should be [url(mask.png)] Pass CSS Transitions with transition: all: property from [none] to [url(mask.png)] at (1) should be [url(mask.png)] Pass CSS Transitions with transition: all: property from [none] to [url(mask.png)] at (1.5) should be [url(mask.png)] -Pass CSS Animations: property from [none] to [url(mask.png)] at (-0.3) should be [none] -Pass CSS Animations: property from [none] to [url(mask.png)] at (0) should be [none] -Pass CSS Animations: property from [none] to [url(mask.png)] at (0.3) should be [none] -Pass CSS Animations: property from [none] to [url(mask.png)] at (0.5) should be [url(mask.png)] -Pass CSS Animations: property from [none] to [url(mask.png)] at (0.6) should be [url(mask.png)] -Pass CSS Animations: property from [none] to [url(mask.png)] at (1) should be [url(mask.png)] -Pass CSS Animations: property from [none] to [url(mask.png)] at (1.5) should be [url(mask.png)] -Pass Web Animations: property from [none] to [url(mask.png)] at (-0.3) should be [none] -Pass Web Animations: property from [none] to [url(mask.png)] at (0) should be [none] -Pass Web Animations: property from [none] to [url(mask.png)] at (0.3) should be [none] -Pass Web Animations: property from [none] to [url(mask.png)] at (0.5) should be [url(mask.png)] -Pass Web Animations: property from [none] to [url(mask.png)] at (0.6) should be [url(mask.png)] -Pass Web Animations: property from [none] to [url(mask.png)] at (1) should be [url(mask.png)] -Pass Web Animations: property from [none] to [url(mask.png)] at (1.5) should be [url(mask.png)] \ No newline at end of file +Fail CSS Animations: property from [none] to [url(mask.png)] at (-0.3) should be [none] +Fail CSS Animations: property from [none] to [url(mask.png)] at (0) should be [none] +Fail CSS Animations: property from [none] to [url(mask.png)] at (0.3) should be [none] +Fail CSS Animations: property from [none] to [url(mask.png)] at (0.5) should be [url(mask.png)] +Fail CSS Animations: property from [none] to [url(mask.png)] at (0.6) should be [url(mask.png)] +Fail CSS Animations: property from [none] to [url(mask.png)] at (1) should be [url(mask.png)] +Fail CSS Animations: property from [none] to [url(mask.png)] at (1.5) should be [url(mask.png)] +Fail Web Animations: property from [none] to [url(mask.png)] at (-0.3) should be [none] +Fail Web Animations: property from [none] to [url(mask.png)] at (0) should be [none] +Fail Web Animations: property from [none] to [url(mask.png)] at (0.3) should be [none] +Fail Web Animations: property from [none] to [url(mask.png)] at (0.5) should be [url(mask.png)] +Fail Web Animations: property from [none] to [url(mask.png)] at (0.6) should be [url(mask.png)] +Fail Web Animations: property from [none] to [url(mask.png)] at (1) should be [url(mask.png)] +Fail Web Animations: property from [none] to [url(mask.png)] at (1.5) should be [url(mask.png)] \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-computed.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-computed.txt index 8e44f9a8005..a26a7f2176b 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-computed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-computed.txt @@ -2,32 +2,32 @@ Harness status: OK Found 27 tests -3 Pass -24 Fail -Pass Property mask value 'none' +12 Pass +15 Fail +Fail Property mask value 'none' Pass Property mask value 'linear-gradient(to left bottom, red, blue)' -Fail Property mask value 'linear-gradient(to left bottom, red, blue) luminance' +Pass Property mask value 'linear-gradient(to left bottom, red, blue) luminance' Pass Property mask value 'url("https://example.com/")' -Fail Property mask value 'linear-gradient(to left bottom, red, blue) 1px 2px' +Pass Property mask value 'linear-gradient(to left bottom, red, blue) 1px 2px' Fail Property mask value 'url("https://example.com/") 1px 2px / contain' -Fail Property mask value 'repeat-y' +Pass Property mask value 'repeat-y' Fail Property mask value 'border-box' Fail Property mask value 'linear-gradient(to left bottom, red, blue) padding-box' Fail Property mask value 'content-box' Fail Property mask value 'url("https://example.com/") fill-box' Fail Property mask value 'linear-gradient(to left bottom, red, blue) stroke-box' Fail Property mask value 'view-box' -Fail Property mask value 'no-clip' -Fail Property mask value 'url("https://example.com/") add' -Fail Property mask value 'subtract' +Pass Property mask value 'no-clip' +Pass Property mask value 'url("https://example.com/") add' +Pass Property mask value 'subtract' Fail Property mask value 'url("https://example.com/") intersect' Fail Property mask value 'linear-gradient(to left bottom, red, blue) exclude' -Fail Property mask value 'alpha' -Fail Property mask value 'url("https://example.com/") alpha' +Pass Property mask value 'alpha' +Pass Property mask value 'url("https://example.com/") alpha' Fail Property mask value 'border-box border-box' Fail Property mask value 'content-box content-box' Fail Property mask value 'border-box content-box' -Fail Property mask value 'border-box no-clip' +Pass Property mask value 'border-box no-clip' Fail Property mask value 'intersect no-clip space round 1px 2px / contain stroke-box linear-gradient(to left bottom, red, blue) luminance' Fail Property mask value 'intersect no-clip space round 1px 2px / contain view-box, stroke-box linear-gradient(to left bottom, red, blue) luminance' -Fail Property mask value 'none alpha' \ No newline at end of file +Pass Property mask value 'none alpha' \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-valid.sub.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-valid.sub.txt index 38edec5ba39..ab9f9bf0561 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-valid.sub.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-masking/parsing/mask-valid.sub.txt @@ -2,60 +2,60 @@ Harness status: OK Found 55 tests -5 Pass -50 Fail -Pass e.style['mask'] = "none" should set the property value +25 Pass +30 Fail +Fail e.style['mask'] = "none" should set the property value Pass e.style['mask'] = "linear-gradient(to left bottom, red, blue)" should set the property value -Fail e.style['mask'] = "linear-gradient(to left bottom, red, blue) luminance" should set the property value +Pass e.style['mask'] = "linear-gradient(to left bottom, red, blue) luminance" should set the property value Pass e.style['mask'] = "url(\"https://wpt.live/\")" should set the property value -Fail e.style['mask'] = "linear-gradient(to left bottom, red, blue) 1px 2px" should set the property value +Pass e.style['mask'] = "linear-gradient(to left bottom, red, blue) 1px 2px" should set the property value Fail e.style['mask'] = "url(\"https://wpt.live/\") 1px 2px / contain" should set the property value -Fail e.style['mask'] = "repeat-y" should set the property value +Pass e.style['mask'] = "repeat-y" should set the property value Fail e.style['mask'] = "border-box" should set the property value Fail e.style['mask'] = "linear-gradient(to left bottom, red, blue) padding-box" should set the property value Fail e.style['mask'] = "content-box" should set the property value Fail e.style['mask'] = "url(\"https://wpt.live/\") fill-box" should set the property value Fail e.style['mask'] = "linear-gradient(to left bottom, red, blue) stroke-box" should set the property value Fail e.style['mask'] = "view-box" should set the property value -Fail e.style['mask'] = "no-clip" should set the property value -Fail e.style['mask'] = "url(\"https://wpt.live/\") add" should set the property value -Fail e.style['mask'] = "subtract" should set the property value +Pass e.style['mask'] = "no-clip" should set the property value +Pass e.style['mask'] = "url(\"https://wpt.live/\") add" should set the property value +Pass e.style['mask'] = "subtract" should set the property value Fail e.style['mask'] = "url(\"https://wpt.live/\") intersect" should set the property value Fail e.style['mask'] = "linear-gradient(to left bottom, red, blue) exclude" should set the property value -Fail e.style['mask'] = "alpha" should set the property value -Fail e.style['mask'] = "url(\"https://wpt.live/\") alpha" should set the property value +Pass e.style['mask'] = "alpha" should set the property value +Pass e.style['mask'] = "url(\"https://wpt.live/\") alpha" should set the property value Fail e.style['mask'] = "border-box border-box" should set the property value Fail e.style['mask'] = "content-box content-box" should set the property value Fail e.style['mask'] = "border-box content-box" should set the property value -Fail e.style['mask'] = "border-box no-clip" should set the property value +Pass e.style['mask'] = "border-box no-clip" should set the property value Fail e.style['mask'] = "intersect no-clip space round 1px 2px / contain stroke-box linear-gradient(to left bottom, red, blue) luminance" should set the property value Fail e.style['mask'] = "intersect no-clip space round 1px 2px / contain view-box, stroke-box linear-gradient(to left bottom, red, blue) luminance" should set the property value -Fail e.style['mask'] = "none alpha" should set the property value +Pass e.style['mask'] = "none alpha" should set the property value Fail e.style['mask'] = "none" should set mask-border-outset Fail e.style['mask'] = "none" should set mask-border-repeat Fail e.style['mask'] = "none" should set mask-border-slice Fail e.style['mask'] = "none" should set mask-border-source Fail e.style['mask'] = "none" should set mask-border-width -Fail e.style['mask'] = "none" should set mask-clip -Fail e.style['mask'] = "none" should set mask-composite +Pass e.style['mask'] = "none" should set mask-clip +Pass e.style['mask'] = "none" should set mask-composite Pass e.style['mask'] = "none" should set mask-image -Fail e.style['mask'] = "none" should set mask-mode -Fail e.style['mask'] = "none" should set mask-origin -Fail e.style['mask'] = "none" should set mask-position -Fail e.style['mask'] = "none" should set mask-repeat -Fail e.style['mask'] = "none" should set mask-size +Pass e.style['mask'] = "none" should set mask-mode +Pass e.style['mask'] = "none" should set mask-origin +Pass e.style['mask'] = "none" should set mask-position +Pass e.style['mask'] = "none" should set mask-repeat +Pass e.style['mask'] = "none" should set mask-size Pass e.style['mask'] = "none" should not set unrelated longhands Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-border-outset Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-border-repeat Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-border-slice Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-border-source Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-border-width -Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-clip +Pass e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-clip Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-composite -Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-image +Pass e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-image Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-mode -Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-origin +Pass e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-origin Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-position Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-repeat Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should set mask-size -Fail e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should not set unrelated longhands \ No newline at end of file +Pass e.style['mask'] = "none, linear-gradient(to left bottom, red, blue) padding-box" should not set unrelated longhands \ No newline at end of file