Skip to content

Commit

Permalink
css: simplify color() duplication fallback logic
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Dec 8, 2023
1 parent 6fa536b commit 824ede6
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 44 deletions.
60 changes: 27 additions & 33 deletions internal/css_parser/css_decls.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
borderRadius := borderRadiusTracker{}
rewrittenRules = make([]css_ast.Rule, 0, len(rules))
didWarnAboutComposes := false
wouldClipColorFlag := false
var declarationKeys map[string]struct{}

// Don't automatically generate the "inset" property if it's not supported
Expand Down Expand Up @@ -118,13 +119,31 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
}
}

for _, rule := range rules {
for i := 0; i < len(rules); i++ {
rule := rules[i]
rewrittenRules = append(rewrittenRules, rule)
decl, ok := rule.Data.(*css_ast.RDeclaration)
if !ok {
continue
}

// If the previous loop iteration would have clipped a color, we duplicate
// it and insert the clipped copy before the unclipped copy. But we only do
// this if there was no previous instance of that property so we avoid
// overwriting any manually-specified fallback values.
var wouldClipColor *bool
if wouldClipColorFlag && p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) {
wouldClipColorFlag = false
clone := *decl
clone.Value = css_ast.CloneTokensWithoutImportRecords(clone.Value)
decl = &clone
rule.Data = decl
n := len(rewrittenRules) - 2
rewrittenRules = append(rewrittenRules[:n], rule, rewrittenRules[n])
} else {
wouldClipColor = &wouldClipColorFlag
}

switch decl.Key {
case css_ast.DComposes:
// Only process "composes" directives if we're in "local-css" or
Expand All @@ -151,19 +170,8 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
}

case css_ast.DBackground:
wouldClamp := false
for i, t := range decl.Value {
decl.Value[i] = p.lowerAndMinifyColor(t, &wouldClamp)
}

// If this contains values outside of sRGB, duplicate and clamp
if wouldClamp && p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) {
clamped := *decl
clamped.Value = css_ast.CloneTokensWithoutImportRecords(clamped.Value)
for i, t := range clamped.Value {
clamped.Value[i] = p.lowerAndMinifyColor(t, nil)
}
rewrittenRules = append(rewrittenRules[:len(rewrittenRules)-1], css_ast.Rule{Loc: rule.Loc, Data: &clamped}, rule)
decl.Value[i] = p.lowerAndMinifyColor(t, wouldClipColor)
}

case css_ast.DBackgroundColor,
Expand All @@ -189,16 +197,7 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
css_ast.DTextEmphasisColor:

if len(decl.Value) == 1 {
wouldClamp := false
decl.Value[0] = p.lowerAndMinifyColor(decl.Value[0], &wouldClamp)

// If this contains values outside of sRGB, duplicate and clamp
if wouldClamp && p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) && len(decl.Value) == 1 {
clamped := *decl
clamped.Value = css_ast.CloneTokensWithoutImportRecords(clamped.Value)
clamped.Value[0] = p.lowerAndMinifyColor(clamped.Value[0], nil)
rewrittenRules = append(rewrittenRules[:len(rewrittenRules)-1], css_ast.Rule{Loc: rule.Loc, Data: &clamped}, rule)
}
decl.Value[0] = p.lowerAndMinifyColor(decl.Value[0], wouldClipColor)
}

case css_ast.DTransform:
Expand All @@ -207,16 +206,7 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
}

case css_ast.DBoxShadow:
wouldClamp := false
decl.Value = p.lowerAndMangleBoxShadows(decl.Value, &wouldClamp)

// If this contains values outside of sRGB, duplicate and clamp
if wouldClamp && p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) {
clamped := *decl
clamped.Value = css_ast.CloneTokensWithoutImportRecords(clamped.Value)
clamped.Value = p.lowerAndMangleBoxShadows(clamped.Value, nil)
rewrittenRules = append(rewrittenRules[:len(rewrittenRules)-1], css_ast.Rule{Loc: rule.Loc, Data: &clamped}, rule)
}
decl.Value = p.lowerAndMangleBoxShadows(decl.Value, wouldClipColor)

// Container name
case css_ast.DContainer:
Expand Down Expand Up @@ -381,6 +371,10 @@ func (p *parser) processDeclarations(rules []css_ast.Rule, composesContext *comp
rewrittenRules = p.insertPrefixedDeclaration(rewrittenRules, "-o-", rule.Loc, decl, declarationKeys)
}
}

if wouldClipColorFlag && p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) {
i -= 1
}
}

// Compact removed rules
Expand Down
8 changes: 4 additions & 4 deletions internal/css_parser/css_decls_box_shadow.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/evanw/esbuild/internal/css_lexer"
)

func (p *parser) lowerAndMangleBoxShadow(tokens []css_ast.Token, wouldClamp *bool) []css_ast.Token {
func (p *parser) lowerAndMangleBoxShadow(tokens []css_ast.Token, wouldClipColor *bool) []css_ast.Token {
insetCount := 0
colorCount := 0
numbersBegin := 0
Expand Down Expand Up @@ -38,7 +38,7 @@ func (p *parser) lowerAndMangleBoxShadow(tokens []css_ast.Token, wouldClamp *boo

if _, ok := parseColor(t); ok {
colorCount++
tokens[i] = p.lowerAndMinifyColor(t, wouldClamp)
tokens[i] = p.lowerAndMinifyColor(t, wouldClipColor)
} else if t.Kind == css_lexer.TIdent && strings.EqualFold(t.Text, "inset") {
insetCount++
} else {
Expand Down Expand Up @@ -78,7 +78,7 @@ func (p *parser) lowerAndMangleBoxShadow(tokens []css_ast.Token, wouldClamp *boo
return tokens
}

func (p *parser) lowerAndMangleBoxShadows(tokens []css_ast.Token, wouldClamp *bool) []css_ast.Token {
func (p *parser) lowerAndMangleBoxShadows(tokens []css_ast.Token, wouldClipColor *bool) []css_ast.Token {
n := len(tokens)
end := 0
i := 0
Expand All @@ -91,7 +91,7 @@ func (p *parser) lowerAndMangleBoxShadows(tokens []css_ast.Token, wouldClamp *bo
}

// Mangle this individual shadow
end += copy(tokens[end:], p.lowerAndMangleBoxShadow(tokens[i:comma], wouldClamp))
end += copy(tokens[end:], p.lowerAndMangleBoxShadow(tokens[i:comma], wouldClipColor))

// Skip over the comma
if comma < n {
Expand Down
14 changes: 7 additions & 7 deletions internal/css_parser/css_decls_color.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func lowerAlphaPercentageToNumber(token css_ast.Token) css_ast.Token {
}

// Convert newer color syntax to older color syntax for older browsers
func (p *parser) lowerAndMinifyColor(token css_ast.Token, wouldClamp *bool) css_ast.Token {
func (p *parser) lowerAndMinifyColor(token css_ast.Token, wouldClipColor *bool) css_ast.Token {
text := token.Text

switch token.Kind {
Expand Down Expand Up @@ -396,14 +396,14 @@ func (p *parser) lowerAndMinifyColor(token css_ast.Token, wouldClamp *bool) css_
case "hwb":
if p.options.unsupportedCSSFeatures.Has(compat.HWB) {
if color, ok := parseColor(token); ok {
return p.tryToGenerateColor(token, color, wouldClamp)
return p.tryToGenerateColor(token, color, wouldClipColor)
}
}

case "color":
if p.options.unsupportedCSSFeatures.Has(compat.ColorFunction) {
if color, ok := parseColor(token); ok {
return p.tryToGenerateColor(token, color, wouldClamp)
return p.tryToGenerateColor(token, color, wouldClipColor)
}
}
}
Expand All @@ -413,7 +413,7 @@ func (p *parser) lowerAndMinifyColor(token css_ast.Token, wouldClamp *bool) css_
// the color because we always print it out using the shortest encoding.
if p.options.minifySyntax {
if hex, ok := parseColor(token); ok {
token = p.tryToGenerateColor(token, hex, wouldClamp)
token = p.tryToGenerateColor(token, hex, wouldClipColor)
}
}

Expand Down Expand Up @@ -740,7 +740,7 @@ func parseColorByte(token css_ast.Token, scale float64) (uint32, bool) {
return uint32(i), ok
}

func (p *parser) tryToGenerateColor(token css_ast.Token, color parsedColor, wouldClamp *bool) css_ast.Token {
func (p *parser) tryToGenerateColor(token css_ast.Token, color parsedColor, wouldClipColor *bool) css_ast.Token {
// Note: Do NOT remove color information from fully transparent colors.
// Safari behaves differently than other browsers for color interpolation:
// https://css-tricks.com/thing-know-gradients-transparent-black/
Expand All @@ -753,8 +753,8 @@ func (p *parser) tryToGenerateColor(token css_ast.Token, color parsedColor, woul
} else {
r, g, b := gam_srgb(xyz_to_lin_srgb(color.x, color.y, color.z))
if r < -0.5/255 || r > 255.5/255 || g < -0.5/255 || g > 255.5/255 || b < -0.5/255 || b > 255.5/255 {
if wouldClamp != nil {
*wouldClamp = true
if wouldClipColor != nil {
*wouldClipColor = true
return token
}
r, g, b = gamut_mapping_xyz_to_srgb(color.x, color.y, color.z)
Expand Down

0 comments on commit 824ede6

Please sign in to comment.