Files
dualfroz eb1d9c9bec fix: memoize css.recursiveCheck to remove exponential-time CSS DoS
Shorthand style property handlers (FontHandler and roughly a dozen
siblings) tokenize a CSS value and feed it to css.recursiveCheck,
which searches for a way to partition the tokens so every group is
accepted by one of a handful of validator functions. The search was
unmemoized: every recursive call is made on a suffix of the token
slice, and the same suffix was re-explored once per partition path
that reached it. For values with several tokens that more than one
validator accepts on their own (e.g. runs of whitespace, which
FontFamilyHandler treats as a valid empty entry), the number of such
paths grows combinatorially with the number of tokens, making
wall-clock cost roughly double per extra token. A short,
attacker-controlled style value can hang a server sanitizing
untrusted HTML.

Memoize recursiveCheck on the length of the remaining token suffix:
distinct suffixes of a given slice always have distinct lengths, and
the outcome for a suffix depends only on its length within one
top-level call (the validator set is constant throughout), so this
caches each of at most len(value) distinct subproblems exactly once.
This does not change any validator's behavior or which values are
accepted or rejected, only how many times each subproblem is solved,
turning the exponential search into a polynomial one.

Closes #225
2026-09-04 05:58:45 +02:00

2047 lines
64 KiB
Go

// Copyright (c) 2019, David Kitchen <david@buro9.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the organisation (Microcosm) nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package css
import (
"regexp"
"strings"
)
var (
defaultStyleHandlers = map[string]func(string) bool{
"align-content": AlignContentHandler,
"align-items": AlignItemsHandler,
"align-self": AlignSelfHandler,
"all": AllHandler,
"animation": AnimationHandler,
"animation-delay": AnimationDelayHandler,
"animation-direction": AnimationDirectionHandler,
"animation-duration": AnimationDurationHandler,
"animation-fill-mode": AnimationFillModeHandler,
"animation-iteration-count": AnimationIterationCountHandler,
"animation-name": AnimationNameHandler,
"animation-play-state": AnimationPlayStateHandler,
"animation-timing-function": TimingFunctionHandler,
"backface-visibility": BackfaceVisibilityHandler,
"background": BackgroundHandler,
"background-attachment": BackgroundAttachmentHandler,
"background-blend-mode": BackgroundBlendModeHandler,
"background-clip": BackgroundClipHandler,
"background-color": ColorHandler,
"background-image": ImageHandler,
"background-origin": BackgroundOriginHandler,
"background-position": BackgroundPositionHandler,
"background-repeat": BackgroundRepeatHandler,
"background-size": BackgroundSizeHandler,
"border": BorderHandler,
"border-bottom": BorderSideHandler,
"border-bottom-color": ColorHandler,
"border-bottom-left-radius": BorderSideRadiusHandler,
"border-bottom-right-radius": BorderSideRadiusHandler,
"border-bottom-style": BorderSideStyleHandler,
"border-bottom-width": BorderSideWidthHandler,
"border-collapse": BorderCollapseHandler,
"border-color": ColorHandler,
"border-image": BorderImageHandler,
"border-image-outset": BorderImageOutsetHandler,
"border-image-repeat": BorderImageRepeatHandler,
"border-image-slice": BorderImageSliceHandler,
"border-image-source": ImageHandler,
"border-image-width": BorderImageWidthHandler,
"border-left": BorderSideHandler,
"border-left-color": ColorHandler,
"border-left-style": BorderSideStyleHandler,
"border-left-width": BorderSideWidthHandler,
"border-radius": BorderRadiusHandler,
"border-right": BorderSideHandler,
"border-right-color": ColorHandler,
"border-right-style": BorderSideStyleHandler,
"border-right-width": BorderSideWidthHandler,
"border-spacing": BorderSpacingHandler,
"border-style": BorderStyleHandler,
"border-top": BorderSideHandler,
"border-top-color": ColorHandler,
"border-top-left-radius": BorderSideRadiusHandler,
"border-top-right-radius": BorderSideRadiusHandler,
"border-top-style": BorderSideStyleHandler,
"border-top-width": BorderSideWidthHandler,
"border-width": BorderWidthHandler,
"bottom": SideHandler,
"box-decoration-break": BoxDecorationBreakHandler,
"box-shadow": BoxShadowHandler,
"box-sizing": BoxSizingHandler,
"break-after": BreakBeforeAfterHandler,
"break-before": BreakBeforeAfterHandler,
"break-inside": BreakInsideHandler,
"caption-side": CaptionSideHandler,
"caret-color": CaretColorHandler,
"clear": ClearHandler,
"clip": ClipHandler,
"color": ColorHandler,
"column-count": ColumnCountHandler,
"column-fill": ColumnFillHandler,
"column-gap": ColumnGapHandler,
"column-rule": ColumnRuleHandler,
"column-rule-color": ColorHandler,
"column-rule-style": BorderSideStyleHandler,
"column-rule-width": ColumnRuleWidthHandler,
"column-span": ColumnSpanHandler,
"column-width": ColumnWidthHandler,
"columns": ColumnsHandler,
"cursor": CursorHandler,
"direction": DirectionHandler,
"display": DisplayHandler,
"empty-cells": EmptyCellsHandler,
"filter": FilterHandler,
"flex": FlexHandler,
"flex-basis": FlexBasisHandler,
"flex-direction": FlexDirectionHandler,
"flex-flow": FlexFlowHandler,
"flex-grow": FlexGrowHandler,
"flex-shrink": FlexGrowHandler,
"flex-wrap": FlexWrapHandler,
"float": FloatHandler,
"font": FontHandler,
"font-family": FontFamilyHandler,
"font-kerning": FontKerningHandler,
"font-language-override": FontLanguageOverrideHandler,
"font-size": FontSizeHandler,
"font-size-adjust": FontSizeAdjustHandler,
"font-stretch": FontStretchHandler,
"font-style": FontStyleHandler,
"font-synthesis": FontSynthesisHandler,
"font-variant": FontVariantHandler,
"font-variant-caps": FontVariantCapsHandler,
"font-variant-position": FontVariantPositionHandler,
"font-weight": FontWeightHandler,
"grid": GridHandler,
"grid-area": GridAreaHandler,
"grid-auto-columns": GridAutoColumnsHandler,
"grid-auto-flow": GridAutoFlowHandler,
"grid-auto-rows": GridAutoColumnsHandler,
"grid-column": GridColumnHandler,
"grid-column-end": GridAxisStartEndHandler,
"grid-column-gap": LengthHandler,
"grid-column-start": GridAxisStartEndHandler,
"grid-gap": GridGapHandler,
"grid-row": GridRowHandler,
"grid-row-end": GridAxisStartEndHandler,
"grid-row-gap": LengthHandler,
"grid-row-start": GridAxisStartEndHandler,
"grid-template": GridTemplateHandler,
"grid-template-areas": GridTemplateAreasHandler,
"grid-template-columns": GridTemplateColumnsHandler,
"grid-template-rows": GridTemplateRowsHandler,
"hanging-punctuation": HangingPunctuationHandler,
"height": HeightHandler,
"hyphens": HyphensHandler,
"image-rendering": ImageRenderingHandler,
"isolation": IsolationHandler,
"justify-content": JustifyContentHandler,
"left": SideHandler,
"letter-spacing": LetterSpacingHandler,
"line-break": LineBreakHandler,
"line-height": LineHeightHandler,
"list-style": ListStyleHandler,
"list-style-image": ImageHandler,
"list-style-position": ListStylePositionHandler,
"list-style-type": ListStyleTypeHandler,
"margin": MarginHandler,
"margin-bottom": MarginSideHandler,
"margin-left": MarginSideHandler,
"margin-right": MarginSideHandler,
"margin-top": MarginSideHandler,
"max-height": MaxHeightWidthHandler,
"max-width": MaxHeightWidthHandler,
"min-height": MinHeightWidthHandler,
"min-width": MinHeightWidthHandler,
"mix-blend-mode": MixBlendModeHandler,
"object-fit": ObjectFitHandler,
"object-position": ObjectPositionHandler,
"opacity": OpacityHandler,
"order": OrderHandler,
"orphans": OrphansHandler,
"outline": OutlineHandler,
"outline-color": ColorHandler,
"outline-offset": OutlineOffsetHandler,
"outline-style": OutlineStyleHandler,
"outline-width": OutlineWidthHandler,
"overflow": OverflowHandler,
"overflow-wrap": OverflowWrapHandler,
"overflow-x": OverflowXYHandler,
"overflow-y": OverflowXYHandler,
"padding": PaddingHandler,
"padding-bottom": PaddingSideHandler,
"padding-left": PaddingSideHandler,
"padding-right": PaddingSideHandler,
"padding-top": PaddingSideHandler,
"page-break-after": PageBreakBeforeAfterHandler,
"page-break-before": PageBreakBeforeAfterHandler,
"page-break-inside": PageBreakInsideHandler,
"perspective": PerspectiveHandler,
"perspective-origin": PerspectiveOriginHandler,
"pointer-events": PointerEventsHandler,
"position": PositionHandler,
"quotes": QuotesHandler,
"resize": ResizeHandler,
"right": SideHandler,
"scroll-behavior": ScrollBehaviorHandler,
"tab-size": TabSizeHandler,
"table-layout": TableLayoutHandler,
"text-align": TextAlignHandler,
"text-align-last": TextAlignLastHandler,
"text-combine-upright": TextCombineUprightHandler,
"text-decoration": TextDecorationHandler,
"text-decoration-color": ColorHandler,
"text-decoration-line": TextDecorationLineHandler,
"text-decoration-style": TextDecorationStyleHandler,
"text-indent": TextIndentHandler,
"text-justify": TextJustifyHandler,
"text-orientation": TextOrientationHandler,
"text-overflow": TextOverflowHandler,
"text-shadow": TextShadowHandler,
"text-transform": TextTransformHandler,
"top": SideHandler,
"transform": TransformHandler,
"transform-origin": TransformOriginHandler,
"transform-style": TransformStyleHandler,
"transition": TransitionHandler,
"transition-delay": TransitionDelayHandler,
"transition-duration": TransitionDurationHandler,
"transition-property": TransitionPropertyHandler,
"transition-timing-function": TimingFunctionHandler,
"unicode-bidi": UnicodeBidiHandler,
"user-select": UserSelectHandler,
"vertical-align": VerticalAlignHandler,
"visibility": VisiblityHandler,
"white-space": WhiteSpaceHandler,
"widows": OrphansHandler,
"width": WidthHandler,
"word-break": WordBreakHandler,
"word-spacing": WordSpacingHandler,
"word-wrap": WordWrapHandler,
"writing-mode": WritingModeHandler,
"z-index": ZIndexHandler,
}
colorValues = []string{"transparent", "initial", "inherit", "aliceblue",
"antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque",
"black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood",
"cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
"cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
"darkgray", "darkgrey", "darkgreen", "darkkhaki", "darkmagenta",
"darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
"darkseagreen", "darkslateblue", "darkslategrey", "darkslategray",
"darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray",
"dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen",
"fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
"grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
"indigo", "ivory", "khaki", "lavender", "lavenderblush",
"lemonchiffon", "lightblue", "lightcoral", "lightcyan",
"lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen",
"lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
"lightslategray", "lightslategrey", "lightsteeelblue", "lightyellow",
"lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
"mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
"mediumslateblue", "mediumspringgreen", "mediumturquoise",
"mediumvioletred", "midnightblue", "mintcream", "mistyrose",
"moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
"orange", "orangered", "orchid", "palegoldenrod", "palegreen",
"paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
"pink", "plum", "powderblue", "purple", "rebeccapurple", "red",
"rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown",
"seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue",
"slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen"}
Alpha = regexp.MustCompile(`^[a-z]+$`)
Blur = regexp.MustCompile(`^blur\([0-9]+px\)$`)
BrightnessCont = regexp.MustCompile(`^(brightness|contrast)\([0-9]+\%\)$`)
Count = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*$`)
CubicBezier = regexp.MustCompile(`^cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)$`)
Digits = regexp.MustCompile(`^digits [2-4]$`)
DropShadow = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`)
Font = regexp.MustCompile(`^('[a-z \-]+'|[a-z \-]+)$`)
Grayscale = regexp.MustCompile(`^grayscale\(([0-9]{1,2}|100)%\)$`)
GridTemplateAreas = regexp.MustCompile(`^['"]?[a-z ]+['"]?$`)
HexRGB = regexp.MustCompile(`^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$`)
HSL = regexp.MustCompile(`^hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)$`)
HSLA = regexp.MustCompile(`^hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)$`)
HueRotate = regexp.MustCompile(`^hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)$`)
Invert = regexp.MustCompile(`^invert\(([0-9]{1,2}|100)%\)$`)
Length = regexp.MustCompile(`^[\-]?([0-9]+|[0-9]*[\.][0-9]+)(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?$`)
Matrix = regexp.MustCompile(`^matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)$`)
Matrix3D = regexp.MustCompile(`^matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)$`)
NegTime = regexp.MustCompile(`^[\-]?[0-9]+[\.]?[0-9]*(s|ms)?$`)
Numeric = regexp.MustCompile(`^[0-9]+$`)
NumericDecimal = regexp.MustCompile(`^[0-9\.]+$`)
Opactiy = regexp.MustCompile(`^opacity\(([0-9]{1,2}|100)%\)$`)
Perspective = regexp.MustCompile(`perspective\(`)
Position = regexp.MustCompile(`^[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*$`)
Opacity = regexp.MustCompile(`^(0[.]?[0-9]*)|(1.0)$`)
QuotedAlpha = regexp.MustCompile(`^["'][a-z]+["']$`)
Quotes = regexp.MustCompile(`^([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+$`)
Rect = regexp.MustCompile(`^rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)$`)
RGB = regexp.MustCompile(`^rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)$`)
RGBA = regexp.MustCompile(`^rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)$`)
Rotate = regexp.MustCompile(`^rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)$`)
Rotate3D = regexp.MustCompile(`^rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)$`)
Saturate = regexp.MustCompile(`^saturate\([0-9]+%\)$`)
Sepia = regexp.MustCompile(`^sepia\(([0-9]{1,2}|100)%\)$`)
Skew = regexp.MustCompile(`skew(x|y)?\(`)
Span = regexp.MustCompile(`^span [0-9]+$`)
Steps = regexp.MustCompile(`^steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)$`)
Time = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*(s|ms)?$`)
TransitionProp = regexp.MustCompile(`^([a-zA-Z]+,[ ]?)*[a-zA-Z]+$`)
TranslateScale = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`)
URL = regexp.MustCompile(`^url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)$`)
ZIndex = regexp.MustCompile(`^[\-]?[0-9]+$`)
)
func multiSplit(value string, seps ...string) []string {
curArray := []string{value}
for _, i := range seps {
newArray := []string{}
for _, j := range curArray {
newArray = append(newArray, strings.Split(j, i)...)
}
curArray = newArray
}
return curArray
}
// recursiveCheck reports whether value can be fully consumed by
// partitioning it, in order, into one or more contiguous groups of
// tokens such that every group is accepted by at least one function in
// funcs.
//
// The search is memoized on the length of the remaining suffix. Every
// recursive call is made on value[i+1:], which is always a suffix of
// the slice originally passed in, and since funcs is constant across
// the whole search, the outcome for a given suffix depends only on how
// many tokens remain in it. Without caching that outcome, the same
// suffix can be re-explored once for every distinct partition path that
// reaches it, which is exponential in len(value) whenever a value has
// several tokens that multiple funcs accept on their own (for example
// runs of whitespace, which some handlers treat as a valid empty
// entry). Caching means each of the at most len(value) distinct
// suffixes is solved once, turning that exponential search into a
// polynomial one without changing which values are ultimately accepted
// or rejected.
func recursiveCheck(value []string, funcs []func(string) bool) bool {
memo := make(map[int]bool, len(value))
return recursiveCheckMemo(value, funcs, memo)
}
func recursiveCheckMemo(value []string, funcs []func(string) bool, memo map[int]bool) bool {
if cached, ok := memo[len(value)]; ok {
return cached
}
result := false
outer:
for i := 0; i < len(value); i++ {
tempVal := strings.Join(value[:i+1], " ")
for _, j := range funcs {
if j(tempVal) && (len(value[i+1:]) == 0 || recursiveCheckMemo(value[i+1:], funcs, memo)) {
result = true
break outer
}
}
}
memo[len(value)] = result
return result
}
func in(value []string, arr []string) bool {
for _, i := range value {
foundString := false
for _, j := range arr {
if j == i {
foundString = true
}
}
if !foundString {
return false
}
}
return true
}
func splitValues(value string) []string {
values := strings.Split(value, ",")
newValues := []string{}
for _, strippedValue := range values {
newValues = append(newValues, strings.ToLower(strings.TrimSpace(strippedValue)))
}
return newValues
}
func GetDefaultHandler(attr string) func(string) bool {
if defaultStyleHandlers[attr] != nil {
return defaultStyleHandlers[attr]
}
return BaseHandler
}
func BaseHandler(value string) bool {
return false
}
func AlignContentHandler(value string) bool {
values := []string{"stretch", "center", "flex-start",
"flex-end", "space-between", "space-around", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AlignItemsHandler(value string) bool {
values := []string{"stretch", "center", "flex-start",
"flex-end", "baseline", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AlignSelfHandler(value string) bool {
values := []string{"auto", "stretch", "center", "flex-start",
"flex-end", "baseline", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AllHandler(value string) bool {
values := []string{"initial", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
AnimationNameHandler,
AnimationDurationHandler,
TimingFunctionHandler,
AnimationDelayHandler,
AnimationIterationCountHandler,
AnimationDirectionHandler,
AnimationFillModeHandler,
AnimationPlayStateHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func AnimationDelayHandler(value string) bool {
if NegTime.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationDirectionHandler(value string) bool {
values := []string{"normal", "reverse", "alternate", "alternate-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationDurationHandler(value string) bool {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationFillModeHandler(value string) bool {
values := []string{"none", "forwards", "backwards", "both", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationIterationCountHandler(value string) bool {
if Count.MatchString(value) {
return true
}
values := []string{"infinite", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationNameHandler(value string) bool {
return Alpha.MatchString(value)
}
func AnimationPlayStateHandler(value string) bool {
values := []string{"paused", "running", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TimingFunctionHandler(value string) bool {
values := []string{"linear", "ease", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
if CubicBezier.MatchString(value) {
return true
}
return Steps.MatchString(value)
}
func BackfaceVisibilityHandler(value string) bool {
values := []string{"visible", "hidden", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if len(strings.Split(i, "/")) == 2 {
newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
} else {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
ColorHandler,
ImageHandler,
BackgroundPositionHandler,
BackgroundSizeHandler,
BackgroundRepeatHandler,
BackgroundOriginHandler,
BackgroundClipHandler,
BackgroundAttachmentHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func BackgroundAttachmentHandler(value string) bool {
values := []string{"scroll", "fixed", "local", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundClipHandler(value string) bool {
values := []string{"border-box", "padding-box", "content-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundBlendModeHandler(value string) bool {
values := []string{"normal", "multiply", "screen", "overlay", "darken",
"lighten", "color-dodge", "saturation", "color", "luminosity"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ImageHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
return URL.MatchString(value)
}
func BackgroundOriginHandler(value string) bool {
values := []string{"padding-box", "border-box", "content-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundPositionHandler(value string) bool {
splitVals := strings.Split(value, ";")
values := []string{"left", "left top", "left bottom", "right", "right top", "right bottom", "right center", "center top", "center center", "center bottom", "center", "top", "bottom", "initial", "inherit"}
if in(splitVals, values) {
return true
}
return Position.MatchString(value)
}
func BackgroundRepeatHandler(value string) bool {
values := []string{"repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundSizeHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"auto", "cover", "contain", "initial", "inherit"}
if in(splitVals, values) {
return true
}
if len(splitVals) > 0 && LengthHandler(splitVals[0]) {
if len(splitVals) < 2 || (len(splitVals) == 2 && LengthHandler(splitVals[1])) {
return true
}
}
return false
}
func BorderHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := multiSplit(value, " ", "/")
usedFunctions := []func(string) bool{
BorderWidthHandler,
BorderStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSideHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
BorderSideWidthHandler,
BorderSideStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSideRadiusHandler(value string) bool {
splitVals := strings.Split(value, " ")
valid := true
for _, i := range splitVals {
if !LengthHandler(i) {
valid = false
break
}
}
if valid {
return true
}
splitVals = splitValues(value)
values := []string{"initial", "inherit"}
return in(splitVals, values)
}
func BorderSideStyleHandler(value string) bool {
values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderSideWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"medium", "thin", "thick", "initial", "inherit"}
return in(splitVals, values)
}
func BorderCollapseHandler(value string) bool {
values := []string{"separate", "collapse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := multiSplit(value, " ", " / ")
usedFunctions := []func(string) bool{
ImageHandler,
BorderImageSliceHandler,
BorderImageWidthHandler,
BorderImageOutsetHandler,
BorderImageRepeatHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderImageOutsetHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageRepeatHandler(value string) bool {
values := []string{"stretch", "repeat", "round", "space", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageSliceHandler(value string) bool {
values := []string{"fill", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderImageWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderRadiusHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSpacingHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderStyleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
BorderSideStyleHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderWidthHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
BorderSideWidthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func SideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BoxDecorationBreakHandler(value string) bool {
values := []string{"slice", "clone", "initial", "initial", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BoxShadowHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
commaSplitVals := strings.Split(value, ",")
for _, val := range commaSplitVals {
splitVals := strings.Split(val, " ")
if len(splitVals) > 6 || len(splitVals) < 2 {
return false
}
if !LengthHandler(splitVals[0]) {
return false
}
if !LengthHandler(splitVals[1]) {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
ColorHandler,
}
if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
return false
}
}
return true
}
func BoxSizingHandler(value string) bool {
values := []string{"slicontent-box", "border-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BreakBeforeAfterHandler(value string) bool {
values := []string{"auto", "avoid", "always", "all", "avoid-page", "page", "left", "right", "recto", "verso", "avoid-column", "column", "avoid-region", "region"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BreakInsideHandler(value string) bool {
values := []string{"auto", "avoid", "avoid-page", "avoid-column", "avoid-region"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func CaptionSideHandler(value string) bool {
values := []string{"top", "bottom", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func CaretColorHandler(value string) bool {
splitVals := splitValues(value)
if in(splitVals, colorValues) {
return true
}
if HexRGB.MatchString(value) {
return true
}
if RGB.MatchString(value) {
return true
}
if RGBA.MatchString(value) {
return true
}
if HSL.MatchString(value) {
return true
}
return HSLA.MatchString(value)
}
func ClearHandler(value string) bool {
values := []string{"none", "left", "right", "both", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ClipHandler(value string) bool {
if Rect.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColorHandler(value string) bool {
splitVals := splitValues(value)
if in(splitVals, colorValues) {
return true
}
if HexRGB.MatchString(value) {
return true
}
if RGB.MatchString(value) {
return true
}
if RGBA.MatchString(value) {
return true
}
if HSL.MatchString(value) {
return true
}
return HSLA.MatchString(value)
}
func ColumnCountHandler(value string) bool {
if Numeric.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnFillHandler(value string) bool {
values := []string{"balance", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnGapHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnRuleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColumnRuleWidthHandler,
BorderSideStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func ColumnRuleWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"medium", "thin", "thick", "initial", "inherit"}
return in(splitVals, values)
}
func ColumnSpanHandler(value string) bool {
values := []string{"none", "all", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"auto", "initial", "inherit"}
return in(splitVals, values)
}
func ColumnsHandler(value string) bool {
values := []string{"auto", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColumnWidthHandler,
ColumnCountHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func CursorHandler(value string) bool {
values := []string{"alias", "all-scroll", "auto", "cell", "context-menu", "col-resize", "copy", "crosshair", "default", "e-resize", "ew-resize", "grab", "grabbing", "help", "move", "n-resize", "ne-resize", "nesw-resize", "ns-resize", "nw-resize", "nwse-resize", "no-drop", "none", "not-allowed", "pointer", "progress", "row-resize", "s-resize", "se-resize", "sw-resize", "text", "vertical-text", "w-resize", "wait", "zoom-in", "zoom-out", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func DirectionHandler(value string) bool {
values := []string{"ltr", "rtl", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func DisplayHandler(value string) bool {
values := []string{"inline", "block", "contents", "flex", "grid", "inline-block", "inline-flex", "inline-grid", "inline-table", "list-item", "run-in", "table", "table-caption", "table-column-group", "table-header-group", "table-footer-group", "table-row-group", "table-cell", "table-column", "table-row", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func EmptyCellsHandler(value string) bool {
values := []string{"show", "hide", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FilterHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
if Blur.MatchString(value) {
return true
}
if BrightnessCont.MatchString(value) {
return true
}
if DropShadow.MatchString(value) {
return true
}
colorValue := strings.TrimSuffix(string(DropShadow.ReplaceAll([]byte(value), []byte{})), ")")
if ColorHandler(colorValue) {
return true
}
if Grayscale.MatchString(value) {
return true
}
if HueRotate.MatchString(value) {
return true
}
if Invert.MatchString(value) {
return true
}
if Opacity.MatchString(value) {
return true
}
if Saturate.MatchString(value) {
return true
}
return Sepia.MatchString(value)
}
func FlexHandler(value string) bool {
values := []string{"auto", "initial", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
FlexGrowHandler,
FlexBasisHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func FlexBasisHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"auto", "initial", "inherit"}
return in(splitVals, values)
}
func FlexDirectionHandler(value string) bool {
values := []string{"row", "row-reverse", "column", "column-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FlexFlowHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
FlexDirectionHandler,
FlexWrapHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func FlexGrowHandler(value string) bool {
if NumericDecimal.MatchString(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"initial", "inherit"}
return in(splitVals, values)
}
func FlexWrapHandler(value string) bool {
values := []string{"nowrap", "wrap", "wrap-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FloatHandler(value string) bool {
values := []string{"none", "left", "right", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontHandler(value string) bool {
values := []string{"caption", "icon", "menu", "message-box", "small-caption", "status-bar", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if len(strings.Split(i, "/")) == 2 {
newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
} else {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
FontStyleHandler,
FontVariantHandler,
FontWeightHandler,
FontSizeHandler,
FontFamilyHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func FontFamilyHandler(value string) bool {
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
for _, i := range splitVals {
i = strings.TrimSpace(i)
if Font.FindString(i) != i {
return false
}
}
return true
}
func FontKerningHandler(value string) bool {
values := []string{"auto", "normal", "none"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontLanguageOverrideHandler(value string) bool {
return Alpha.MatchString(value)
}
func FontSizeHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"medium", "xx-small", "x-small", "small", "large", "x-large", "xx-large", "smaller", "larger", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontSizeAdjustHandler(value string) bool {
if Count.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontStretchHandler(value string) bool {
values := []string{"ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontStyleHandler(value string) bool {
values := []string{"normal", "italic", "oblique", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontSynthesisHandler(value string) bool {
values := []string{"none", "style", "weight"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantCapsHandler(value string) bool {
values := []string{"normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantHandler(value string) bool {
values := []string{"normal", "small-caps", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantPositionHandler(value string) bool {
values := []string{"normal", "sub", "super"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontWeightHandler(value string) bool {
values := []string{"normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if i != "/" {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
GridTemplateRowsHandler,
GridTemplateColumnsHandler,
GridTemplateAreasHandler,
GridAutoColumnsHandler,
GridAutoFlowHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func GridAreaHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridAutoColumnsHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "max-content", "min-content", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridAutoFlowHandler(value string) bool {
values := []string{"row", "column", "dense", "row dense", "column dense"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridColumnHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridColumnGapHandler(value string) bool {
return LengthHandler(value)
}
func LengthHandler(value string) bool {
return Length.MatchString(value)
}
func LineBreakHandler(value string) bool {
values := []string{"auto", "loose", "normal", "strict"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridAxisStartEndHandler(value string) bool {
if Numeric.MatchString(value) {
return true
}
if Span.MatchString(value) {
return true
}
values := []string{"auto"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridGapHandler(value string) bool {
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridColumnGapHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridRowHandler(value string) bool {
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridTemplateHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridTemplateColumnsHandler,
GridTemplateRowsHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridTemplateAreasHandler(value string) bool {
values := []string{"none"}
if in([]string{value}, values) {
return true
}
return GridTemplateAreas.MatchString(value)
}
func GridTemplateColumnsHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"none", "auto", "max-content", "min-content", "initial", "inherit"}
for _, val := range splitVals {
if LengthHandler(val) {
continue
}
valArr := []string{val}
if !in(valArr, values) {
return false
}
}
return true
}
func GridTemplateRowsHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"none", "auto", "max-content", "min-content"}
for _, val := range splitVals {
if LengthHandler(val) {
continue
}
valArr := []string{val}
if !in(valArr, values) {
return false
}
}
return true
}
func HangingPunctuationHandler(value string) bool {
values := []string{"none", "first", "last", "allow-end", "force-end", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func HeightHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func HyphensHandler(value string) bool {
values := []string{"none", "manual", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ImageRenderingHandler(value string) bool {
values := []string{"auto", "smooth", "high-quality", "crisp-edges", "pixelated"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func IsolationHandler(value string) bool {
values := []string{"auto", "isolate", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func JustifyContentHandler(value string) bool {
values := []string{"flex-start", "flex-end", "center", "space-between", "space-around", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func LetterSpacingHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func LineHeightHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ListStyleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ListStyleTypeHandler,
ListStylePositionHandler,
ImageHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func ListStylePositionHandler(value string) bool {
values := []string{"inside", "outside", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ListStyleTypeHandler(value string) bool {
values := []string{"disc", "armenian", "circle", "cjk-ideographic", "decimal", "decimal-leading-zero", "georgian", "hebrew", "hiragana", "hiragana-iroha", "katakana", "katakana-iroha", "lower-alpha", "lower-greek", "lower-latin", "lower-roman", "none", "square", "upper-alpha", "upper-greek", "upper-latin", "upper-roman", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MarginHandler(value string) bool {
values := []string{"auto", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
MarginSideHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func MarginSideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MaxHeightWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MinHeightWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MixBlendModeHandler(value string) bool {
values := []string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "difference", "exclusion", "hue", "saturation", "color", "luminosity"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ObjectFitHandler(value string) bool {
values := []string{"fill", "contain", "cover", "none", "scale-down", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ObjectPositionHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func OpacityHandler(value string) bool {
if Opacity.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OrderHandler(value string) bool {
if Numeric.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColorHandler,
OutlineWidthHandler,
OutlineStyleHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func OutlineOffsetHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineStyleHandler(value string) bool {
values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"medium", "thin", "thick", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowHandler(value string) bool {
values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowXYHandler(value string) bool {
values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowWrapHandler(value string) bool {
values := []string{"normal", "break-word", "anywhere"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OrphansHandler(value string) bool {
return Numeric.MatchString(value)
}
func PaddingHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
PaddingSideHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func PaddingSideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PageBreakBeforeAfterHandler(value string) bool {
values := []string{"auto", "always", "avoid", "left", "right", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PageBreakInsideHandler(value string) bool {
values := []string{"auto", "avoid", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PerspectiveHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PerspectiveOriginHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
xValues := []string{"left", "center", "right"}
yValues := []string{"top", "center", "bottom"}
if len(splitVals) > 1 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
} else if len(splitVals) == 1 {
return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
}
return false
}
func PointerEventsHandler(value string) bool {
values := []string{"auto", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PositionHandler(value string) bool {
values := []string{"static", "absolute", "fixed", "relative", "sticky", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func QuotesHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
return Quotes.MatchString(value)
}
func ResizeHandler(value string) bool {
values := []string{"none", "both", "horizontal", "vertical", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ScrollBehaviorHandler(value string) bool {
values := []string{"auto", "smooth", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TabSizeHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TableLayoutHandler(value string) bool {
values := []string{"auto", "fixed", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextAlignHandler(value string) bool {
values := []string{"left", "right", "center", "justify", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextAlignLastHandler(value string) bool {
values := []string{"auto", "left", "right", "center", "justify", "start", "end", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextCombineUprightHandler(value string) bool {
values := []string{"none", "all"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
return Digits.MatchString(value)
}
func TextDecorationHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
TextDecorationStyleHandler,
ColorHandler,
TextDecorationLineHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func TextDecorationLineHandler(value string) bool {
values := []string{"none", "underline", "overline", "line-through", "initial", "inherit"}
splitVals := strings.Split(value, " ")
return in(splitVals, values)
}
func TextDecorationStyleHandler(value string) bool {
values := []string{"solid", "double", "dotted", "dashed", "wavy", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextIndentHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextJustifyHandler(value string) bool {
values := []string{"auto", "inter-word", "inter-character", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextOverflowHandler(value string) bool {
if QuotedAlpha.MatchString(value) {
return true
}
values := []string{"clip", "ellipsis", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextOrientationHandler(value string) bool {
values := []string{"mixed", "upright", "sideways", "sideways-right"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextShadowHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
commaSplitVals := strings.Split(value, ",")
for _, val := range commaSplitVals {
splitVals := strings.Split(val, " ")
if len(splitVals) > 6 || len(splitVals) < 2 {
return false
}
if !LengthHandler(splitVals[0]) {
return false
}
if !LengthHandler(splitVals[1]) {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
ColorHandler,
}
if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
return false
}
}
return true
}
func TextTransformHandler(value string) bool {
values := []string{"none", "capitalize", "uppercase", "lowercase", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransformHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
if Matrix.MatchString(value) {
return true
}
if Matrix3D.MatchString(value) {
return true
}
subValue := string(TranslateScale.ReplaceAll([]byte(value), []byte{}))
trimValue := strings.Split(strings.TrimSuffix(subValue, ")"), ",")
valid := true
for _, i := range trimValue {
if !LengthHandler(strings.TrimSpace(i)) {
valid = false
break
}
}
if valid && trimValue != nil {
return true
}
if Rotate.MatchString(value) {
return true
}
if Rotate3D.MatchString(value) {
return true
}
subValue = string(Skew.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
trimValue = strings.Split(subValue, ",")
valid = true
for _, i := range trimValue {
if !LengthHandler(strings.TrimSpace(i)) {
valid = false
break
}
}
if valid {
return true
}
subValue = string(Perspective.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
return LengthHandler(subValue)
}
func TransformOriginHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
xValues := []string{"left", "center", "right"}
yValues := []string{"top", "center", "bottom"}
if len(splitVals) > 2 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
if !in([]string{splitVals[1]}, yValues) && !LengthHandler(splitVals[1]) {
return false
}
return LengthHandler(splitVals[2])
} else if len(splitVals) > 1 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
} else if len(splitVals) == 1 {
return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
}
return false
}
func TransformStyleHandler(value string) bool {
values := []string{"flat", "preserve-3d", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
TransitionPropertyHandler,
TransitionDurationHandler,
TimingFunctionHandler,
TransitionDelayHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func TransitionDelayHandler(value string) bool {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionDurationHandler(value string) bool {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionPropertyHandler(value string) bool {
if TransitionProp.MatchString(value) {
return true
}
values := []string{"none", "all", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func UnicodeBidiHandler(value string) bool {
values := []string{"normal", "embed", "bidi-override", "isolate", "isolate-override", "plaintext", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func UserSelectHandler(value string) bool {
values := []string{"auto", "none", "text", "all"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func VerticalAlignHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func VisiblityHandler(value string) bool {
values := []string{"visible", "hidden", "collapse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WhiteSpaceHandler(value string) bool {
values := []string{"normal", "nowrap", "pre", "pre-line", "pre-wrap", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordSpacingHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordBreakHandler(value string) bool {
values := []string{"normal", "break-all", "keep-all", "break-word", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordWrapHandler(value string) bool {
values := []string{"normal", "break-word", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WritingModeHandler(value string) bool {
values := []string{"horizontal-tb", "vertical-rl", "vertical-lr"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ZIndexHandler(value string) bool {
if ZIndex.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}