diff --git a/node/test/visitor.test.mjs b/node/test/visitor.test.mjs index c763b8409..77d51b9d6 100644 --- a/node/test/visitor.test.mjs +++ b/node/test/visitor.test.mjs @@ -308,7 +308,7 @@ test('spacing with env substitution', () => { } }); - assert.equal(res.code.toString(), '.test{background:var(--foo) var(--bar);border:var(--foo)solid;transform:scale(1.5) scale(1.5);padding:10px 20px;margin:10px auto;outline:red solid;cursor:url(cursor.png) 4 12, auto;stroke-dasharray:5 10 15;counter-increment:myCounter 2;background:linear-gradient(red 25%, blue 75%);content:"hello" " world";width:calc(10px - 20px)}'); + assert.equal(res.code.toString(), '.test{background:var(--foo) var(--bar);border:var(--foo)solid;transform:scale(1.5) scale(1.5);padding:10px 20px;margin:10px auto;outline:red solid;cursor:url(cursor.png) 4 12, auto;stroke-dasharray:5 10 15;counter-increment:myCounter 2;background:linear-gradient(red 25%, #00f 75%);content:"hello" " world";width:calc(10px - 20px)}'); }); test('url', () => { diff --git a/src/lib.rs b/src/lib.rs index eefef7682..45a677cef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13768,8 +13768,8 @@ mod tests { div { background-image: radial-gradient(to left, white, black), repeating-linear-gradient(to bottom right, black, white), repeating-radial-gradient(to top, aqua, red); } - .old-radial { - background: radial-gradient(0 50%, ellipse farthest-corner, black, white); + .old-webkit-radial { + background: -webkit-radial-gradient(0 50%, ellipse farthest-corner, #000, #fff); } .simple1 { background: linear-gradient(black, white); @@ -13783,8 +13783,8 @@ mod tests { .simple4 { background: linear-gradient(to right top, black, white); } - .direction { - background: linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); + .direction-old-webkit-gradient { + background: -webkit-linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); } .silent { background: -webkit-linear-gradient(top left, black, white); @@ -13893,11 +13893,11 @@ mod tests { } div { - background-image: radial-gradient(to left, white, black), repeating-linear-gradient(to bottom right, black, white), repeating-radial-gradient(to top, aqua, red); + background-image: radial-gradient(to left, #fff, #000), repeating-linear-gradient(to bottom right, #000, #fff), repeating-radial-gradient(to top, #0ff, red); } - .old-radial { - background: radial-gradient(0 50%, ellipse farthest-corner, black, white); + .old-webkit-radial { + background: -webkit-radial-gradient(0 50%, ellipse farthest-corner, #000, #fff); } .simple1 { @@ -13928,8 +13928,8 @@ mod tests { background: linear-gradient(to top right, #000, #fff); } - .direction { - background: linear-gradient(top left, black, rgba(0, 0, 0, .5), white); + .direction-old-webkit-gradient { + background: -webkit-linear-gradient(top left, #000, rgba(0, 0, 0, .5), #fff); } .silent { @@ -14023,11 +14023,11 @@ mod tests { } .cover { - background: radial-gradient(ellipse cover at center, white, black); + background: radial-gradient(ellipse cover at center, #fff, #000); } .contain { - background: radial-gradient(contain at center, white, black); + background: radial-gradient(contain at center, #fff, #000); } .no-div { @@ -20548,18 +20548,23 @@ mod tests { &format!("color({} 7 -20.5 100 / 0)", result_color_space), ); + minify_test( + ".foo { border: lch(from rebeccapurple l c calc(h + 180)); }", + ".foo{border:lch(from #639 l c calc(h + 180))}", + ); + // https://github.com/web-platform-tests/wpt/blob/master/css/css-color/parsing/relative-color-invalid.html minify_test( - ".foo{color:rgb(from rebeccapurple r 10deg 10)}", - ".foo{color:rgb(from rebeccapurple r 10deg 10)}", + ".foo{color: rgb(from rebeccapurple r 10deg 10) }", + ".foo{color:rgb(from #639 r 10deg 10)}", ); minify_test( - ".foo{color:rgb(from rebeccapurple l g b)}", - ".foo{color:rgb(from rebeccapurple l g b)}", + ".foo{ color: rgb(from rebeccapurple l g b) }", + ".foo{color:rgb(from #639 l g b)}", ); minify_test( - ".foo{color:hsl(from rebeccapurple s h l)}", - ".foo{color:hsl(from rebeccapurple s h l)}", + ".foo{color: hsl(from rebeccapurple s h l) }", + ".foo{color:hsl(from #639 s h l)}", ); minify_test(".foo{color:hsl(from rebeccapurple s s s / s)}", ".foo{color:#bfaa40}"); minify_test( @@ -20667,19 +20672,19 @@ mod tests { ); minify_test( ".foo { color: color-mix(in srgb, currentColor, blue); }", - ".foo{color:color-mix(in srgb, currentColor, blue)}", + ".foo{color:color-mix(in srgb, currentColor, #00f)}", ); minify_test( ".foo { color: color-mix(in srgb, blue, currentColor); }", - ".foo{color:color-mix(in srgb, blue, currentColor)}", + ".foo{color:color-mix(in srgb, #00f, currentColor)}", ); minify_test( ".foo { color: color-mix(in srgb, accentcolor, blue); }", - ".foo{color:color-mix(in srgb, accentcolor, blue)}", + ".foo{color:color-mix(in srgb, accentcolor, #00f)}", ); minify_test( - ".foo { color: color-mix(in srgb, blue, accentcolor); }", - ".foo{color:color-mix(in srgb, blue, accentcolor)}", + ".foo { color: color-mix(in srgb, rebeccapurple, accentcolor); }", + ".foo{color:color-mix(in srgb, #639, accentcolor)}", ); // regex for converting web platform tests: @@ -23091,6 +23096,149 @@ mod tests { ".foo { height: calc(var(--spectrum-global-dimension-size-300) / 2);", ".foo{height:calc(var(--spectrum-global-dimension-size-300) / 2)}", ); + + minify_test( + ".foo { mask: linear-gradient(90deg, white, var(--bar, rgba(0, 0, 0, .5)) ) }", + ".foo{mask:linear-gradient(90deg, #fff, var(--bar,#00000080))}", + ); + minify_test( + ".foo { background-image: var( --foo, linear-gradient(white, rgba(0 0 0 / 50%)) ) }", + ".foo{background-image:var(--foo,linear-gradient(#fff, #00000080))}", + ); + + // Test in `light-dark()` + minify_test( + ".foo{background: light-dark(var(--c, white), black)}", + ".foo{background:light-dark(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background: light-dark(foo(var(--c, white)), black)}", + ".foo{background:light-dark(foo(var(--c,white)),#000)}", + ); + minify_test( + ".foo{background: color-mix(in lch, var(--c, white) 50%, black)}", + ".foo{background:color-mix(in lch, var(--c,#fff) 50%, #000)}", + ); + + // Test minify color in functions + minify_test( + ".foo{background-image:linear-gradient(var(--c,white),black)}", + ".foo{background-image:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:repeating-linear-gradient(var(--c,white),black)}", + ".foo{background-image:repeating-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:radial-gradient(var(--c,white),black)}", + ".foo{background-image:radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:repeating-radial-gradient(var(--c,white),black)}", + ".foo{background-image:repeating-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:conic-gradient(var(--c,white),black)}", + ".foo{background-image:conic-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:repeating-conic-gradient(var(--c,white),black)}", + ".foo{background-image:repeating-conic-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:image-set(linear-gradient(var(--c,white),black) 1x)}", + ".foo{background-image:image-set(linear-gradient(var(--c,#fff),#000) 1x)}", + ); + minify_test( + ".foo{background-image:-webkit-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-webkit-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-webkit-repeating-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-webkit-repeating-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-webkit-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-webkit-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-webkit-repeating-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-webkit-repeating-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-moz-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-moz-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-moz-repeating-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-moz-repeating-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-moz-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-moz-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-moz-repeating-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-moz-repeating-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-o-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-o-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-o-repeating-linear-gradient(var(--c,white),black)}", + ".foo{background-image:-o-repeating-linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-o-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-o-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-o-repeating-radial-gradient(var(--c,white),black)}", + ".foo{background-image:-o-repeating-radial-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{background-image:-webkit-image-set(linear-gradient(var(--c,white),black) 1x)}", + ".foo{background-image:-webkit-image-set(linear-gradient(var(--c,#fff),#000) 1x)}", + ); + minify_test( + ".foo{border-image-source:linear-gradient(var(--c,white),black)}", + ".foo{border-image-source:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{border-image:linear-gradient(var(--c,white),black) 30}", + ".foo{border-image:linear-gradient(var(--c,#fff),#000) 30}", + ); + minify_test( + ".foo{list-style:linear-gradient(var(--c,white),black)}", + ".foo{list-style:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{list-style-image:linear-gradient(var(--c,white),black)}", + ".foo{list-style-image:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{mask-image:linear-gradient(var(--c,white),black)}", + ".foo{mask-image:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{mask-border-source:linear-gradient(var(--c,white),black)}", + ".foo{mask-border-source:linear-gradient(var(--c,#fff),#000)}", + ); + minify_test( + ".foo{mask-border:linear-gradient(var(--c,white),black) 30}", + ".foo{mask-border:linear-gradient(var(--c,#fff),#000) 30}", + ); + minify_test( + ".foo{-webkit-mask-box-image:linear-gradient(var(--c,white),black) 30}", + ".foo{-webkit-mask-box-image:linear-gradient(var(--c,#fff),#000) 30}", + ); + minify_test( + ".foo{-webkit-mask-box-image-source:linear-gradient(var(--c,white),black)}", + ".foo{-webkit-mask-box-image-source:linear-gradient(var(--c,#fff),#000)}", + ); + + // Test minify color in var() fallbak minify_test( ".foo { color: var(--color, rgb(255, 255, 0)); }", ".foo{color:var(--color,#ff0)}", @@ -23099,10 +23247,195 @@ mod tests { ".foo { color: var(--color, #ffff00); }", ".foo{color:var(--color,#ff0)}", ); + minify_test( + ".foo { color: var(--color, rgb(255 255 255 / .9)); }", + ".foo{color:var(--color,#ffffffe6)}", + ); + minify_test( + ".foo { color: var(--color, hsl(0deg 100% 50%)); }", + ".foo{color:var(--color,red)}", + ); + minify_test( + ".test-color { color: var(--c, white); }", + ".test-color{color:var(--c,#fff)}", + ); + minify_test( + ".test-background-color { background-color: var(--c, white); }", + ".test-background-color{background-color:var(--c,#fff)}", + ); + minify_test( + ".test-background { background: var(--c, white); }", + ".test-background{background:var(--c,#fff)}", + ); + minify_test( + ".test-border { border: var(--c, white); }", + ".test-border{border:var(--c,#fff)}", + ); + minify_test( + ".test-border-top { border-top: var(--c, white); }", + ".test-border-top{border-top:var(--c,#fff)}", + ); + minify_test( + ".test-border-right { border-right: var(--c, white); }", + ".test-border-right{border-right:var(--c,#fff)}", + ); + minify_test( + ".test-border-bottom { border-bottom: var(--c, white); }", + ".test-border-bottom{border-bottom:var(--c,#fff)}", + ); + minify_test( + ".test-border-left { border-left: var(--c, white); }", + ".test-border-left{border-left:var(--c,#fff)}", + ); + minify_test( + ".test-border-block { border-block: var(--c, white); }", + ".test-border-block{border-block:var(--c,#fff)}", + ); + minify_test( + ".test-border-block-start { border-block-start: var(--c, white); }", + ".test-border-block-start{border-block-start:var(--c,#fff)}", + ); + minify_test( + ".test-border-block-end { border-block-end: var(--c, white); }", + ".test-border-block-end{border-block-end:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline { border-inline: var(--c, white); }", + ".test-border-inline{border-inline:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline-start { border-inline-start: var(--c, white); }", + ".test-border-inline-start{border-inline-start:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline-end { border-inline-end: var(--c, white); }", + ".test-border-inline-end{border-inline-end:var(--c,#fff)}", + ); + minify_test( + ".test-border-color { border-color: var(--c, white); }", + ".test-border-color{border-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-block-color { border-block-color: var(--c, white); }", + ".test-border-block-color{border-block-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline-color { border-inline-color: var(--c, white); }", + ".test-border-inline-color{border-inline-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-top-color { border-top-color: var(--c, white); }", + ".test-border-top-color{border-top-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-bottom-color { border-bottom-color: var(--c, white); }", + ".test-border-bottom-color{border-bottom-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-left-color { border-left-color: var(--c, white); }", + ".test-border-left-color{border-left-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-right-color { border-right-color: var(--c, white); }", + ".test-border-right-color{border-right-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-block-start-color { border-block-start-color: var(--c, white); }", + ".test-border-block-start-color{border-block-start-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-block-end-color { border-block-end-color: var(--c, white); }", + ".test-border-block-end-color{border-block-end-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline-start-color { border-inline-start-color: var(--c, white); }", + ".test-border-inline-start-color{border-inline-start-color:var(--c,#fff)}", + ); + minify_test( + ".test-border-inline-end-color { border-inline-end-color: var(--c, white); }", + ".test-border-inline-end-color{border-inline-end-color:var(--c,#fff)}", + ); + minify_test( + ".test-outline { outline: var(--c, white); }", + ".test-outline{outline:var(--c,#fff)}", + ); + minify_test( + ".test-outline-color { outline-color: var(--c, white); }", + ".test-outline-color{outline-color:var(--c,#fff)}", + ); + minify_test( + ".test-box-shadow { box-shadow: var(--s, 0 0 white); }", + ".test-box-shadow{box-shadow:var(--s,0 0 #fff)}", + ); + minify_test( + ".test-text-shadow { text-shadow: var(--s, 0 0 white); }", + ".test-text-shadow{text-shadow:var(--s,0 0 #fff)}", + ); + minify_test( + ".test-text-decoration-color { text-decoration-color: var(--c, white); }", + ".test-text-decoration-color{text-decoration-color:var(--c,#fff)}", + ); + minify_test( + ".test-text-decoration { text-decoration: var(--c, white); }", + ".test-text-decoration{text-decoration:var(--c,#fff)}", + ); + minify_test( + ".test-text-emphasis-color { text-emphasis-color: var(--c, white); }", + ".test-text-emphasis-color{text-emphasis-color:var(--c,#fff)}", + ); + minify_test( + ".test-text-emphasis { text-emphasis: var(--c, white); }", + ".test-text-emphasis{text-emphasis:var(--c,#fff)}", + ); + minify_test( + ".test-caret-color { caret-color: var(--c, currentcolor); }", + ".test-caret-color{caret-color:var(--c,currentColor)}", + ); + minify_test( + ".test-accent-color { accent-color: var(--c, transparent); }", + ".test-accent-color{accent-color:var(--c,#0000)}", + ); + minify_test( + ".test-fill { fill: var(--c, white); }", + ".test-fill{fill:var(--c,#fff)}", + ); + minify_test( + ".test-stroke { stroke: var(--c, white); }", + ".test-stroke{stroke:var(--c,#fff)}", + ); minify_test( ".foo { color: var(--color, rgb(var(--red), var(--green), 0)); }", ".foo{color:var(--color,rgb(var(--red), var(--green), 0))}", ); + // 非白名单内的函数颜色值保持原状 + minify_test( + ".foo{background: foo(var(--c, blue))}", + ".foo{background:foo(var(--c,blue))}", + ); + minify_test( + ".foo2{background: linear-gradient(foo(var(--c, blue)), red)}", + ".foo2{background:linear-gradient(foo(var(--c,blue)), red)}", + ); + minify_test( + ".foo3{background: linear-gradient(var(--c, blue),red)}", + ".foo3{background:linear-gradient(var(--c,#00f),red)}", + ); + minify_test( + ".foo4{--x: 90px; background: linear-gradient(red var(--x, white), blue)}", + ".foo4{--x:90px;background:linear-gradient(red var(--x,#fff), #00f)}", + ); + + // 非 color 相关的属性颜色值保持原状 + minify_test( + ".foo { width: var(--w, white); height: var(--h, transparent); }", + ".foo{width:var(--w,white);height:var(--h,transparent)}", + ); + // supports 中的颜色值保持原状 + minify_test( + "@supports (color: var(--c, white)) { .foo { color: red; } }", + "@supports (color:var(--c, white)){.foo{color:red}}", + ); + minify_test(".foo { --test: .5s; }", ".foo{--test:.5s}"); minify_test(".foo { --theme-sizes-1\\/12: 2 }", ".foo{--theme-sizes-1\\/12:2}"); minify_test(".foo { --test: 0px; }", ".foo{--test:0px}"); @@ -23132,6 +23465,32 @@ mod tests { ".foo { width: attr(data-width type(), 100px); }", ".foo{width:attr(data-width type(), 100px)}", ); + minify_test( + ".foo { color: attr(lightblue type(), blue); }", + // TODO: Once we implement structured parsing for attr(), we can minify the blue color. + ".foo{color:attr(lightblue type(), blue)}", + ); + + // TODO var() in attr() + // minify_test( + // ".foo { color: attr(lightblue type(), var(--foo, hsl(0deg 100% 50%))); }", + // ".foo{color:attr(lightblue type(), red)}", + // ); + minify_test( + ".foo { color: var(--lightblue, blue); }", + ".foo{color:var(--lightblue,#00f)}", + ); + + // Edge cases for color keyword minification in var() fallbacks + // System colors should be preserved + minify_test( + ".foo { color: var(--c, ButtonText); }", + ".foo{color:var(--c,ButtonText)}", + ); + // Case-insensitive color names + minify_test(".foo { color: var(--c, WHITE); }", ".foo{color:var(--c,#fff)}"); + // Invalid color names should be preserved + minify_test(".foo { color: var(--c, unknowColor); }", ".foo{color:var(--c,unknowColor)}"); minify_test(".foo { width: attr( data-foo % ); }", ".foo{width:attr(data-foo %)}"); @@ -29913,7 +30272,27 @@ mod tests { } } "#, - "@container style(color:yellow){.foo{color:red}}", + "@container style(color:#ff0){.foo{color:red}}", + ); + minify_test( + r#" + @container style(color: var(--bar, blue)) { + .foo { + color: white; + } + } + "#, + "@container style(color:var(--bar,#00f)){.foo{color:#fff}}", + ); + minify_test( + r#" + @container style(background: linear-gradient(red, blue)) { + .foo { + color: white; + } + } + "#, + "@container style(background:linear-gradient(red,#00f)){.foo{color:#fff}}", ); minify_test( r#" @@ -29925,6 +30304,26 @@ mod tests { "#, "@container style(--foo:){.foo{color:red}}", ); + minify_test( + r#" + @container style(--foo: var(--bar, blue)) { + .foo { + color: white; + } + } + "#, + "@container style(--foo:var(--bar,blue)){.foo{color:#fff}}", + ); + minify_test( + r#" + @container style(--foo: linear-gradient(red, blue)) { + .foo { + color: white; + } + } + "#, + "@container style(--foo:linear-gradient(red, blue)){.foo{color:#fff}}", + ); minify_test( r#" @container style(--foo: ) { diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 26da05708..8f9bc9003 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -24,7 +24,7 @@ use crate::values::time::Time; use crate::values::url::Url; #[cfg(feature = "visitor")] use crate::visitor::Visit; -use cssparser::color::parse_hash_color; +use cssparser::color::{parse_hash_color, parse_named_color}; use cssparser::*; use super::AnimationName; @@ -152,8 +152,13 @@ impl<'i> UnparsedProperty<'i> { input: &mut Parser<'i, 't>, options: &ParserOptions<'_, 'i>, ) -> Result>> { + let color_ident_mode = if property_id.parse_color_idents_in_unparsed() { + ColorIdentMode::FunctionsWhitelist + } else { + ColorIdentMode::None + }; let value = input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| { - TokenList::parse(input, options, 0) + TokenList::parse_with_color_idents(input, options, 0, color_ident_mode) })?; Ok(UnparsedProperty { property_id, value }) } @@ -278,6 +283,37 @@ impl<'a> std::hash::Hash for TokenOrValue<'a> { } } +#[derive(Clone, Copy, Debug)] +struct TokenListParseOptions { + color_ident_mode: ColorIdentMode, + // true at top-level token list, false when parsing arguments of any CSS function. + not_in_css_fn: bool, + allow_function_color_idents: bool, +} + +impl TokenListParseOptions { + fn top_level(color_ident_mode: ColorIdentMode) -> Self { + Self { + color_ident_mode, + not_in_css_fn: true, + allow_function_color_idents: false, + } + } + + fn in_css_function(color_ident_mode: ColorIdentMode, allow_function_color_idents: bool) -> Self { + Self { + color_ident_mode, + not_in_css_fn: false, + allow_function_color_idents, + } + } + + fn allow_color_idents(self) -> bool { + self.color_ident_mode == ColorIdentMode::FunctionsWhitelist + && (self.not_in_css_fn || self.allow_function_color_idents) + } +} + impl<'i> ParseWithOptions<'i> for TokenList<'i> { fn parse_with_options<'t>( input: &mut Parser<'i, 't>, @@ -292,9 +328,33 @@ impl<'i> TokenList<'i> { input: &mut Parser<'i, 't>, options: &ParserOptions<'_, 'i>, depth: usize, + ) -> Result>> { + TokenList::read_token_list(input, options, depth, TokenListParseOptions::top_level(ColorIdentMode::None)) + } + + pub(crate) fn parse_with_color_idents<'t>( + input: &mut Parser<'i, 't>, + options: &ParserOptions<'_, 'i>, + depth: usize, + color_ident_mode: ColorIdentMode, + ) -> Result>> { + TokenList::read_token_list(input, options, depth, TokenListParseOptions::top_level(color_ident_mode)) + } + + fn read_token_list<'t>( + input: &mut Parser<'i, 't>, + options: &ParserOptions<'_, 'i>, + depth: usize, + parse_options: TokenListParseOptions, ) -> Result>> { let mut tokens = vec![]; - TokenList::parse_into(input, &mut tokens, options, depth)?; + TokenList::parse_into( + input, + &mut tokens, + options, + depth, + parse_options, + )?; // Slice off leading and trailing whitespace if there are at least two tokens. // If there is only one token, we must preserve it. e.g. `--foo: ;` is valid. @@ -365,6 +425,7 @@ impl<'i> TokenList<'i> { tokens: &mut Vec>, options: &ParserOptions<'_, 'i>, depth: usize, + parse_options: TokenListParseOptions, ) -> Result<(), ParseError<'i, ParserError<'i>>> { if depth > 500 { return Err(input.new_custom_error(ParserError::MaximumNestingDepth)); @@ -376,16 +437,22 @@ impl<'i> TokenList<'i> { Ok(&cssparser::Token::Function(ref f)) => { // Attempt to parse embedded color values into hex tokens. let f = f.into(); + let allow_function_color_idents = parse_options.color_ident_mode == ColorIdentMode::FunctionsWhitelist + && allow_parser_color_whitelist(&f); + let function_parse_options = + TokenListParseOptions::in_css_function(parse_options.color_ident_mode, allow_function_color_idents); if let Some(color) = try_parse_color_token(&f, &state, input) { tokens.push(TokenOrValue::Color(color)); - } else if let Ok(color) = input.try_parse(|input| UnresolvedColor::parse(&f, input, options)) { + } else if let Ok(color) = + input.try_parse(|input| UnresolvedColor::parse(&f, input, options, function_parse_options)) + { tokens.push(TokenOrValue::UnresolvedColor(color)); } else if f == "url" { input.reset(&state); tokens.push(TokenOrValue::Url(Url::parse(input)?)); } else if f == "var" { let var = input.parse_nested_block(|input| { - let var = Variable::parse(input, options, depth + 1)?; + let var = Variable::parse(input, options, depth + 1, parse_options)?; Ok(TokenOrValue::Var(var)) })?; tokens.push(var); @@ -396,7 +463,14 @@ impl<'i> TokenList<'i> { })?; tokens.push(env); } else { - let arguments = input.parse_nested_block(|input| TokenList::parse(input, options, depth + 1))?; + let arguments = input.parse_nested_block(|input| { + TokenList::read_token_list( + input, + options, + depth + 1, + function_parse_options, + ) + })?; tokens.push(TokenOrValue::Function(Function { name: Ident(f), arguments, @@ -414,8 +488,17 @@ impl<'i> TokenList<'i> { input.reset(&state); tokens.push(TokenOrValue::Url(Url::parse(input)?)); } - Ok(&cssparser::Token::Ident(ref name)) if name.starts_with("--") => { - tokens.push(TokenOrValue::DashedIdent(name.into())); + Ok(&cssparser::Token::Ident(ref name)) => { + if let Some(color) = try_parse_color_ident(name.as_ref(), parse_options) { + tokens.push(TokenOrValue::Color(color)); + continue; + } + + if name.starts_with("--") { + tokens.push(TokenOrValue::DashedIdent(name.into())); + } else { + tokens.push(Token::Ident(name.into()).into()); + } } Ok(token @ &cssparser::Token::ParenthesisBlock) | Ok(token @ &cssparser::Token::SquareBracketBlock) @@ -428,7 +511,15 @@ impl<'i> TokenList<'i> { _ => unreachable!(), }; - input.parse_nested_block(|input| TokenList::parse_into(input, tokens, options, depth + 1))?; + input.parse_nested_block(|input| { + TokenList::parse_into( + input, + tokens, + options, + depth + 1, + parse_options, + ) + })?; tokens.push(closing_delimiter.into()); } @@ -469,21 +560,91 @@ fn try_parse_color_token<'i, 't>( state: &ParserState, input: &mut Parser<'i, 't>, ) -> Option { - match_ignore_ascii_case! { &*f, - "rgb" | "rgba" | "hsl" | "hsla" | "hwb" | "lab" | "lch" | "oklab" | "oklch" | "color" | "color-mix" | "light-dark" => { - let s = input.state(); - input.reset(&state); - if let Ok(color) = CssColor::parse(input) { - return Some(color) - } - input.reset(&s); - }, - _ => {} + if has_color_function(f.as_ref()) { + let s = input.state(); + input.reset(&state); + if let Ok(color) = CssColor::parse(input) { + return Some(color); + } + input.reset(&s); } None } +#[inline] +fn try_parse_color_ident(ident: &str, parse_options: TokenListParseOptions) -> Option { + if !parse_options.allow_color_idents() { + return None; + } + match_ignore_ascii_case! { ident, + "currentcolor" => Some(CssColor::CurrentColor), + "transparent" => Some(CssColor::RGBA(RGBA::transparent())), + _ => { + if let Ok((r, g, b)) = parse_named_color(ident) { + Some(CssColor::RGBA(RGBA { red: r, green: g, blue: b, alpha: 255 })) + } else { + // Preserve system color casing in unparsed values (no size win from normalizing). + None + } + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum ColorIdentMode { + None, + FunctionsWhitelist, +} + +#[inline] +// 允许解析颜色的函数 +fn allow_parser_color_whitelist(name: &CowArcStr<'_>) -> bool { + has_color_function(name.as_ref()) +} + +#[inline] +fn has_color_function(name: &str) -> bool { + match_ignore_ascii_case! { name, + "rgb" + | "rgba" + | "hsl" + | "hsla" + | "hwb" + | "lab" + | "lch" + | "oklab" + | "oklch" + | "color" + | "color-mix" + | "light-dark" + // TODO: Support contrast-color( ) + // https://drafts.csswg.org/css-color-5/#contrast-color + | "linear-gradient" + | "repeating-linear-gradient" + | "radial-gradient" + | "repeating-radial-gradient" + | "conic-gradient" + | "repeating-conic-gradient" + | "-webkit-linear-gradient" + | "-webkit-repeating-linear-gradient" + | "-webkit-gradient" + | "-webkit-radial-gradient" + | "-webkit-repeating-radial-gradient" + | "-moz-linear-gradient" + | "-moz-repeating-linear-gradient" + | "-moz-radial-gradient" + | "-moz-repeating-radial-gradient" + | "-o-linear-gradient" + | "-o-repeating-linear-gradient" + | "-o-radial-gradient" + | "-o-repeating-radial-gradient" + | "image-set" + | "-webkit-image-set" => true, + _ => false + } +} + impl<'i> TokenList<'i> { pub(crate) fn to_css(&self, dest: &mut Printer, is_custom_property: bool) -> Result<(), PrinterError> where @@ -1196,11 +1357,12 @@ impl<'i> Variable<'i> { input: &mut Parser<'i, 't>, options: &ParserOptions<'_, 'i>, depth: usize, + parse_options: TokenListParseOptions, ) -> Result>> { let name = DashedIdentReference::parse_with_options(input, options)?; let fallback = if input.try_parse(|input| input.expect_comma()).is_ok() { - Some(TokenList::parse(input, options, depth)?) + Some(TokenList::read_token_list(input, options, depth, parse_options)?) } else { None }; @@ -1507,6 +1669,7 @@ impl<'i> UnresolvedColor<'i> { f: &CowArcStr<'i>, input: &mut Parser<'i, 't>, options: &ParserOptions<'_, 'i>, + parse_options: TokenListParseOptions, ) -> Result>> { let mut parser = ComponentParser::new(false); match_ignore_ascii_case! { &*f, @@ -1539,10 +1702,10 @@ impl<'i> UnresolvedColor<'i> { "light-dark" => { input.parse_nested_block(|input| { let light = input.parse_until_before(Delimiter::Comma, |input| - TokenList::parse(input, options, 0) + TokenList::read_token_list(input, options, 0, parse_options) )?; input.expect_comma()?; - let dark = TokenList::parse(input, options, 0)?; + let dark = TokenList::read_token_list(input, options, 0, parse_options)?; Ok(UnresolvedColor::LightDark { light, dark }) }) }, diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 54c47548a..df69b3a53 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -141,6 +141,7 @@ use crate::visitor::Visit; use align::*; use animation::*; use background::*; +use bitflags::bitflags; use border::*; use border_image::*; use border_radius::*; @@ -148,6 +149,14 @@ use box_shadow::*; use contain::*; use css_modules::*; use cssparser::*; + +bitflags! { + #[derive(Default)] + pub(crate) struct ValueFeatures: u8 { + const MINIFY_COLORS_IN_FN = 1 << 0; + const IMAGE = 1 << 1; + } +} use custom::*; use display::*; use effects::*; @@ -173,7 +182,7 @@ macro_rules! define_properties { ( $( $(#[$meta: meta])* - $name: literal: $property: ident($type: ty $(, $vp: ty)?) $( / $prefix: ident )* $( unprefixed: $unprefixed: literal )? $( options: $options: literal )? $( shorthand: $shorthand: literal )? $( [ logical_group: $logical_group: ident, category: $logical_category: ident ] )? $( if $condition: ident )?, + $name: literal: $property: ident($type: ty $(, $vp: ty)?) $( / $prefix: ident )* $( unprefixed: $unprefixed: literal )? $( options: $options: literal )? $( value_features: [ $($value_features: ident),+ ] )? $( shorthand: $shorthand: literal )? $( [ logical_group: $logical_group: ident, category: $logical_category: ident ] )? $( if $condition: ident )?, )+ ) => { /// A CSS property id. @@ -398,6 +407,24 @@ macro_rules! define_properties { } } + pub(crate) fn value_features(&self) -> ValueFeatures { + use PropertyId::*; + match self { + $( + $(#[$meta])* + $property$((vp_name!($vp, _p)))? => { + let features = ValueFeatures::empty() $(| $(ValueFeatures::$value_features)|+ )?; + features + }, + )+ + _ => ValueFeatures::empty(), + } + } + + pub(crate) fn parse_color_idents_in_unparsed(&self) -> bool { + self.value_features().contains(ValueFeatures::MINIFY_COLORS_IN_FN) + } + /// Returns whether a property is a shorthand. pub fn is_shorthand(&self) -> bool { $( @@ -1200,8 +1227,8 @@ macro_rules! define_properties { } define_properties! { - "background-color": BackgroundColor(CssColor), - "background-image": BackgroundImage(SmallVec<[Image<'i>; 1]>), + "background-color": BackgroundColor(CssColor) value_features: [MINIFY_COLORS_IN_FN], + "background-image": BackgroundImage(SmallVec<[Image<'i>; 1]>) value_features: [MINIFY_COLORS_IN_FN], "background-position-x": BackgroundPositionX(SmallVec<[HorizontalPosition; 1]>), "background-position-y": BackgroundPositionY(SmallVec<[VerticalPosition; 1]>), "background-position": BackgroundPosition(SmallVec<[BackgroundPosition; 1]>) shorthand: true, @@ -1210,11 +1237,11 @@ define_properties! { "background-attachment": BackgroundAttachment(SmallVec<[BackgroundAttachment; 1]>), "background-clip": BackgroundClip(SmallVec<[BackgroundClip; 1]>, VendorPrefix) / WebKit / Moz, "background-origin": BackgroundOrigin(SmallVec<[BackgroundOrigin; 1]>), - "background": Background(SmallVec<[Background<'i>; 1]>) shorthand: true, + "background": Background(SmallVec<[Background<'i>; 1]>) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, - "box-shadow": BoxShadow(SmallVec<[BoxShadow; 1]>, VendorPrefix) / WebKit / Moz, + "box-shadow": BoxShadow(SmallVec<[BoxShadow; 1]>, VendorPrefix) / WebKit / Moz value_features: [MINIFY_COLORS_IN_FN], "opacity": Opacity(AlphaValue), - "color": Color(CssColor), + "color": Color(CssColor) value_features: [MINIFY_COLORS_IN_FN], "display": Display(Display), "visibility": Visibility(Visibility), @@ -1254,14 +1281,14 @@ define_properties! { "border-spacing": BorderSpacing(Size2D), - "border-top-color": BorderTopColor(CssColor) [logical_group: BorderColor, category: Physical], - "border-bottom-color": BorderBottomColor(CssColor) [logical_group: BorderColor, category: Physical], - "border-left-color": BorderLeftColor(CssColor) [logical_group: BorderColor, category: Physical], - "border-right-color": BorderRightColor(CssColor) [logical_group: BorderColor, category: Physical], - "border-block-start-color": BorderBlockStartColor(CssColor) [logical_group: BorderColor, category: Logical], - "border-block-end-color": BorderBlockEndColor(CssColor) [logical_group: BorderColor, category: Logical], - "border-inline-start-color": BorderInlineStartColor(CssColor) [logical_group: BorderColor, category: Logical], - "border-inline-end-color": BorderInlineEndColor(CssColor) [logical_group: BorderColor, category: Logical], + "border-top-color": BorderTopColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Physical], + "border-bottom-color": BorderBottomColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Physical], + "border-left-color": BorderLeftColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Physical], + "border-right-color": BorderRightColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Physical], + "border-block-start-color": BorderBlockStartColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Logical], + "border-block-end-color": BorderBlockEndColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Logical], + "border-inline-start-color": BorderInlineStartColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Logical], + "border-inline-end-color": BorderInlineEndColor(CssColor) value_features: [MINIFY_COLORS_IN_FN] [logical_group: BorderColor, category: Logical], "border-top-style": BorderTopStyle(LineStyle) [logical_group: BorderStyle, category: Physical], "border-bottom-style": BorderBottomStyle(LineStyle) [logical_group: BorderStyle, category: Physical], @@ -1291,39 +1318,39 @@ define_properties! { "border-end-end-radius": BorderEndEndRadius(Size2D) [logical_group: BorderRadius, category: Logical], "border-radius": BorderRadius(BorderRadius, VendorPrefix) / WebKit / Moz shorthand: true, - "border-image-source": BorderImageSource(Image<'i>), + "border-image-source": BorderImageSource(Image<'i>) value_features: [MINIFY_COLORS_IN_FN], "border-image-outset": BorderImageOutset(Rect), "border-image-repeat": BorderImageRepeat(BorderImageRepeat), "border-image-width": BorderImageWidth(Rect), "border-image-slice": BorderImageSlice(BorderImageSlice), - "border-image": BorderImage(BorderImage<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true, + "border-image": BorderImage(BorderImage<'i>, VendorPrefix) / WebKit / Moz / O value_features: [MINIFY_COLORS_IN_FN] shorthand: true, - "border-color": BorderColor(BorderColor) shorthand: true, + "border-color": BorderColor(BorderColor) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "border-style": BorderStyle(BorderStyle) shorthand: true, "border-width": BorderWidth(BorderWidth) shorthand: true, - "border-block-color": BorderBlockColor(BorderBlockColor) shorthand: true, + "border-block-color": BorderBlockColor(BorderBlockColor) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "border-block-style": BorderBlockStyle(BorderBlockStyle) shorthand: true, "border-block-width": BorderBlockWidth(BorderBlockWidth) shorthand: true, - "border-inline-color": BorderInlineColor(BorderInlineColor) shorthand: true, + "border-inline-color": BorderInlineColor(BorderInlineColor) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "border-inline-style": BorderInlineStyle(BorderInlineStyle) shorthand: true, "border-inline-width": BorderInlineWidth(BorderInlineWidth) shorthand: true, - "border": Border(Border) shorthand: true, - "border-top": BorderTop(BorderTop) shorthand: true, - "border-bottom": BorderBottom(BorderBottom) shorthand: true, - "border-left": BorderLeft(BorderLeft) shorthand: true, - "border-right": BorderRight(BorderRight) shorthand: true, - "border-block": BorderBlock(BorderBlock) shorthand: true, - "border-block-start": BorderBlockStart(BorderBlockStart) shorthand: true, - "border-block-end": BorderBlockEnd(BorderBlockEnd) shorthand: true, - "border-inline": BorderInline(BorderInline) shorthand: true, - "border-inline-start": BorderInlineStart(BorderInlineStart) shorthand: true, - "border-inline-end": BorderInlineEnd(BorderInlineEnd) shorthand: true, - - "outline": Outline(Outline) shorthand: true, - "outline-color": OutlineColor(CssColor), + "border": Border(Border) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-top": BorderTop(BorderTop) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-bottom": BorderBottom(BorderBottom) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-left": BorderLeft(BorderLeft) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-right": BorderRight(BorderRight) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-block": BorderBlock(BorderBlock) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-block-start": BorderBlockStart(BorderBlockStart) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-block-end": BorderBlockEnd(BorderBlockEnd) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-inline": BorderInline(BorderInline) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-inline-start": BorderInlineStart(BorderInlineStart) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "border-inline-end": BorderInlineEnd(BorderInlineEnd) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + + "outline": Outline(Outline) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "outline-color": OutlineColor(CssColor) value_features: [MINIFY_COLORS_IN_FN], "outline-style": OutlineStyle(OutlineStyle), "outline-width": OutlineWidth(BorderSideWidth), @@ -1500,15 +1527,15 @@ define_properties! { // https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506 "text-decoration-line": TextDecorationLine(TextDecorationLine, VendorPrefix) / WebKit / Moz, "text-decoration-style": TextDecorationStyle(TextDecorationStyle, VendorPrefix) / WebKit / Moz, - "text-decoration-color": TextDecorationColor(CssColor, VendorPrefix) / WebKit / Moz, + "text-decoration-color": TextDecorationColor(CssColor, VendorPrefix) / WebKit / Moz value_features: [MINIFY_COLORS_IN_FN], "text-decoration-thickness": TextDecorationThickness(TextDecorationThickness), - "text-decoration": TextDecoration(TextDecoration, VendorPrefix) / WebKit / Moz shorthand: true, + "text-decoration": TextDecoration(TextDecoration, VendorPrefix) / WebKit / Moz value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "text-decoration-skip-ink": TextDecorationSkipInk(TextDecorationSkipInk, VendorPrefix) / WebKit, "text-emphasis-style": TextEmphasisStyle(TextEmphasisStyle<'i>, VendorPrefix) / WebKit, - "text-emphasis-color": TextEmphasisColor(CssColor, VendorPrefix) / WebKit, - "text-emphasis": TextEmphasis(TextEmphasis<'i>, VendorPrefix) / WebKit shorthand: true, + "text-emphasis-color": TextEmphasisColor(CssColor, VendorPrefix) / WebKit value_features: [MINIFY_COLORS_IN_FN], + "text-emphasis": TextEmphasis(TextEmphasis<'i>, VendorPrefix) / WebKit value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "text-emphasis-position": TextEmphasisPosition(TextEmphasisPosition, VendorPrefix) / WebKit, - "text-shadow": TextShadow(SmallVec<[TextShadow; 1]>), + "text-shadow": TextShadow(SmallVec<[TextShadow; 1]>) value_features: [MINIFY_COLORS_IN_FN], // https://w3c.github.io/csswg-drafts/css-size-adjust/ "text-size-adjust": TextSizeAdjust(TextSizeAdjust, VendorPrefix) / WebKit / Moz / Ms, @@ -1523,28 +1550,28 @@ define_properties! { // https://www.w3.org/TR/2021/WD-css-ui-4-20210316 "resize": Resize(Resize), "cursor": Cursor(Cursor<'i>), - "caret-color": CaretColor(ColorOrAuto), + "caret-color": CaretColor(ColorOrAuto) value_features: [MINIFY_COLORS_IN_FN], "caret-shape": CaretShape(CaretShape), "caret": Caret(Caret) shorthand: true, "user-select": UserSelect(UserSelect, VendorPrefix) / WebKit / Moz / Ms, - "accent-color": AccentColor(ColorOrAuto), + "accent-color": AccentColor(ColorOrAuto) value_features: [MINIFY_COLORS_IN_FN], "appearance": Appearance(Appearance<'i>, VendorPrefix) / WebKit / Moz / Ms, // https://www.w3.org/TR/2020/WD-css-lists-3-20201117 "list-style-type": ListStyleType(ListStyleType<'i>), - "list-style-image": ListStyleImage(Image<'i>), + "list-style-image": ListStyleImage(Image<'i>) value_features: [MINIFY_COLORS_IN_FN], "list-style-position": ListStylePosition(ListStylePosition), - "list-style": ListStyle(ListStyle<'i>) shorthand: true, + "list-style": ListStyle(ListStyle<'i>) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, "marker-side": MarkerSide(MarkerSide), // CSS modules "composes": Composes(Composes<'i>) if css_modules, // https://www.w3.org/TR/SVG2/painting.html - "fill": Fill(SVGPaint<'i>), + "fill": Fill(SVGPaint<'i>) value_features: [MINIFY_COLORS_IN_FN], "fill-rule": FillRule(FillRule), "fill-opacity": FillOpacity(AlphaValue), - "stroke": Stroke(SVGPaint<'i>), + "stroke": Stroke(SVGPaint<'i>) value_features: [MINIFY_COLORS_IN_FN], "stroke-opacity": StrokeOpacity(AlphaValue), "stroke-width": StrokeWidth(LengthPercentage), "stroke-linecap": StrokeLinecap(StrokeLinecap), @@ -1566,7 +1593,7 @@ define_properties! { // https://www.w3.org/TR/css-masking-1/ "clip-path": ClipPath(ClipPath<'i>, VendorPrefix) / WebKit, "clip-rule": ClipRule(FillRule), - "mask-image": MaskImage(SmallVec<[Image<'i>; 1]>, VendorPrefix) / WebKit, + "mask-image": MaskImage(SmallVec<[Image<'i>; 1]>, VendorPrefix) / WebKit value_features: [MINIFY_COLORS_IN_FN], "mask-mode": MaskMode(SmallVec<[MaskMode; 1]>), "mask-repeat": MaskRepeat(SmallVec<[BackgroundRepeat; 1]>, VendorPrefix) / WebKit, "mask-position-x": MaskPositionX(SmallVec<[HorizontalPosition; 1]>), @@ -1577,20 +1604,20 @@ define_properties! { "mask-size": MaskSize(SmallVec<[BackgroundSize; 1]>, VendorPrefix) / WebKit, "mask-composite": MaskComposite(SmallVec<[MaskComposite; 1]>), "mask-type": MaskType(MaskType), - "mask": Mask(SmallVec<[Mask<'i>; 1]>, VendorPrefix) / WebKit shorthand: true, - "mask-border-source": MaskBorderSource(Image<'i>), + "mask": Mask(SmallVec<[Mask<'i>; 1]>, VendorPrefix) / WebKit value_features: [MINIFY_COLORS_IN_FN] shorthand: true, + "mask-border-source": MaskBorderSource(Image<'i>) value_features: [MINIFY_COLORS_IN_FN], "mask-border-mode": MaskBorderMode(MaskBorderMode), "mask-border-slice": MaskBorderSlice(BorderImageSlice), "mask-border-width": MaskBorderWidth(Rect), "mask-border-outset": MaskBorderOutset(Rect), "mask-border-repeat": MaskBorderRepeat(BorderImageRepeat), - "mask-border": MaskBorder(MaskBorder<'i>) shorthand: true, + "mask-border": MaskBorder(MaskBorder<'i>) value_features: [MINIFY_COLORS_IN_FN] shorthand: true, // WebKit additions "-webkit-mask-composite": WebKitMaskComposite(SmallVec<[WebKitMaskComposite; 1]>), "mask-source-type": WebKitMaskSourceType(SmallVec<[WebKitMaskSourceType; 1]>, VendorPrefix) / WebKit unprefixed: false, - "mask-box-image": WebKitMaskBoxImage(BorderImage<'i>, VendorPrefix) / WebKit unprefixed: false, - "mask-box-image-source": WebKitMaskBoxImageSource(Image<'i>, VendorPrefix) / WebKit unprefixed: false, + "mask-box-image": WebKitMaskBoxImage(BorderImage<'i>, VendorPrefix) / WebKit unprefixed: false value_features: [MINIFY_COLORS_IN_FN], + "mask-box-image-source": WebKitMaskBoxImageSource(Image<'i>, VendorPrefix) / WebKit unprefixed: false value_features: [MINIFY_COLORS_IN_FN], "mask-box-image-slice": WebKitMaskBoxImageSlice(BorderImageSlice, VendorPrefix) / WebKit unprefixed: false, "mask-box-image-width": WebKitMaskBoxImageWidth(Rect, VendorPrefix) / WebKit unprefixed: false, "mask-box-image-outset": WebKitMaskBoxImageOutset(Rect, VendorPrefix) / WebKit unprefixed: false,