Skip to content

Commit

Permalink
feat: Improve CSS validator to support new property values supported …
Browse files Browse the repository at this point in the history
…in browser

With this change, Vivliostyle.js can support new CSS property values supported in browser,
including support for new color value syntax defined in CSS Color Level 4.

- resolves #940
  • Loading branch information
MurakamiShinyu committed Jul 13, 2022
1 parent 8aac989 commit 1d5f493
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 92 deletions.
44 changes: 2 additions & 42 deletions packages/core/src/vivliostyle/assets.ts
Expand Up @@ -204,49 +204,9 @@ ANGLE = POS_ANGLE | ZERO | NEGATIVE;
LENGTH_OR_NUM = LENGTH | NUM;
ANGLE_OR_NUM = ANGLE | NUM;
MIN_MAX_FIT_CONTENT = min-content | max-content | fit-content;
SCOLOR = HASHCOLOR | aliceblue: #F0F8FF | antiquewhite: #FAEBD7 | aqua: #00FFFF | aquamarine: #7FFFD4 | azure: #F0FFFF |
beige: #F5F5DC | bisque: #FFE4C4 | black: #000000 | blanchedalmond: #FFEBCD | blue: #0000FF | blueviolet: #8A2BE2 | brown: #A52A2A |
burlywood: #DEB887 | cadetblue: #5F9EA0 | chartreuse: #7FFF00 | chocolate: #D2691E | coral: #FF7F50 | cornflowerblue: #6495ED |
cornsilk: #FFF8DC | crimson: #DC143C | cyan: #00FFFF | darkblue: #00008B | darkcyan: #008B8B | darkgoldenrod: #B8860B |
darkgray: #A9A9A9 | darkgreen: #006400 | darkgrey: #A9A9A9 | darkkhaki: #BDB76B | darkmagenta: #8B008B | darkolivegreen: #556B2F |
darkorange: #FF8C00 | darkorchid: #9932CC | darkred: #8B0000 | darksalmon: #E9967A | darkseagreen: #8FBC8F | darkslateblue: #483D8B |
darkslategray: #2F4F4F | darkslategrey: #2F4F4F | darkturquoise: #00CED1 | darkviolet: #9400D3 | deeppink: #FF1493 |
deepskyblue: #00BFFF | dimgray: #696969 | dimgrey: #696969 | dodgerblue: #1E90FF | firebrick: #B22222 | floralwhite: #FFFAF0 |
forestgreen: #228B22 | fuchsia: #FF00FF | gainsboro: #DCDCDC | ghostwhite: #F8F8FF | gold: #FFD700 | goldenrod: #DAA520 |
gray: #808080 | green: #008000 | greenyellow: #ADFF2F | grey: #808080 | honeydew: #F0FFF0 | hotpink: #FF69B4 | indianred: #CD5C5C |
indigo: #4B0082 | ivory: #FFFFF0 | khaki: #F0E68C | lavender: #E6E6FA | lavenderblush: #FFF0F5 | lawngreen: #7CFC00 |
lemonchiffon: #FFFACD | lightblue: #ADD8E6 | lightcoral: #F08080 | lightcyan: #E0FFFF | lightgoldenrodyellow: #FAFAD2 |
lightgray: #D3D3D3 | lightgreen: #90EE90 | lightgrey: #D3D3D3 | lightpink: #FFB6C1 | lightsalmon: #FFA07A | lightseagreen: #20B2AA |
lightskyblue: #87CEFA | lightslategray: #778899 | lightslategrey: #778899 | lightsteelblue: #B0C4DE | lightyellow: #FFFFE0 |
lime: #00FF00 | limegreen: #32CD32 | linen: #FAF0E6 | magenta: #FF00FF | maroon: #800000 | mediumaquamarine: #66CDAA |
mediumblue: #0000CD | mediumorchid: #BA55D3 | mediumpurple: #9370DB | mediumseagreen: #3CB371 | mediumslateblue: #7B68EE |
mediumspringgreen: #00FA9A | mediumturquoise: #48D1CC | mediumvioletred: #C71585 | midnightblue: #191970 | mintcream: #F5FFFA |
mistyrose: #FFE4E1 | moccasin: #FFE4B5 | navajowhite: #FFDEAD | navy: #000080 | oldlace: #FDF5E6 | olive: #808000 |
olivedrab: #6B8E23 | orange: #FFA500 | orangered: #FF4500 | orchid: #DA70D6 | palegoldenrod: #EEE8AA | palegreen: #98FB98 |
paleturquoise: #AFEEEE | palevioletred: #DB7093 | papayawhip: #FFEFD5 | peachpuff: #FFDAB9 | peru: #CD853F | pink: #FFC0CB |
plum: #DDA0DD | powderblue: #B0E0E6 | purple: #800080 | rebeccapurple: #663399 | red: #FF0000 | rosybrown: #BC8F8F | royalblue: #4169E1 |
saddlebrown: #8B4513 | salmon: #FA8072 | sandybrown: #F4A460 | seagreen: #2E8B57 | seashell: #FFF5EE | sienna: #A0522D |
silver: #C0C0C0 | skyblue: #87CEEB | slateblue: #6A5ACD | slategray: #708090 | slategrey: #708090 | snow: #FFFAFA |
springgreen: #00FF7F | steelblue: #4682B4 | tan: #D2B48C | teal: #008080 | thistle: #D8BFD8 | tomato: #FF6347 |
turquoise: #40E0D0 | violet: #EE82EE | wheat: #F5DEB3 | white: #FFFFFF | whitesmoke: #F5F5F5 | yellow: #FFFF00 |
yellowgreen: #9ACD32 | transparent | currentcolor;
RGBCOLOR = rgb(INT{3}) | rgb(STRICT_PERCENTAGE{3});
RGBACOLOR = rgba(NUM{4}) | rgba(STRICT_PERCENTAGE{3} NUM);
HSLCOLOR = hsl(NUM PERCENTAGE{2});
HSLACOLOR = hsl(NUM PERCENTAGE{2} NUM);
COLOR = SCOLOR | RGBCOLOR | RGBACOLOR | HSLCOLOR | HSLACOLOR;
BG_POSITION_TERM = PLENGTH | left | center | right | top | bottom;
SIDE_OR_CORNER = [left | right] || [top | bottom];
COLOR_STOP = SPACE(COLOR [PERCENTAGE | LENGTH]?);
LINEAR_GRADIENT = linear-gradient([ANGLE | SPACE(to SIDE_OR_CORNER)]? COLOR_STOP+) |
repeating-linear-gradient([ANGLE | SPACE(to SIDE_OR_CORNER)]? COLOR_STOP+) |;
GRADIENT_EXTENT = closest-corner | closest-side | farthest-corner | farthest-side;
GRADIENT_POSITION = at BG_POSITION_TERM{1,4};
GRADIENT_SHAPE = SPACE(circle LENGTH? GRADIENT_POSITION?) | SPACE(ellipse PLENGTH{2}? GRADIENT_POSITION?)| SPACE([circle | ellipse] GRADIENT_EXTENT? GRADIENT_POSITION?);
RADIAL_GRADIENT = radial-gradient([GRADIENT_SHAPE | SPACE(GRADIENT_POSITION)]? COLOR_STOP+) |
repeating-radial-gradient([GRADIENT_SHAPE | SPACE(GRADIENT_POSITION)]? COLOR_STOP+);
URI_OR_NONE = URI | none;
IMAGE = URI | LINEAR_GRADIENT | RADIAL_GRADIENT | none;
IMAGE = URI | IMAGE_FUNCTION | none;
background-attachment = COMMA( [scroll | fixed | local]+ );
background-color = COLOR;
background-image = COMMA( IMAGE+ );
Expand Down Expand Up @@ -318,7 +278,7 @@ FAMILY_LIST = COMMA( FAMILY+ );
font-family = FAMILY_LIST;
font-size = xx-small | x-small | small | medium | large | x-large | xx-large | larger | smaller | PPLENGTH | POS_NUM;
font-style = normal | italic | oblique;
font-weight = normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
font-weight = normal | bold | bolder | lighter | POS_NUM;
height = PAPLENGTH | POS_NUM | MIN_MAX_FIT_CONTENT;
left = APLENGTH;
letter-spacing = normal | LENGTH_OR_NUM;
Expand Down
37 changes: 1 addition & 36 deletions packages/core/src/vivliostyle/css-parser.ts
Expand Up @@ -70,30 +70,6 @@ export enum StylesheetFlavor {
AUTHOR = "Author",
}

/**
* CSS Color value from hash text (without '#' character).
*/
export function colorFromHash(text: string): Css.Color {
let num = parseInt(text, 16);
if (isNaN(num)) {
throw new Error("E_CSS_COLOR");
}
if (text.length == 6) {
return new Css.Color(num);
}
if (text.length == 3) {
num =
(num & 15) |
((num & 15) << 4) |
((num & 240) << 4) |
((num & 240) << 8) |
((num & 3840) << 8) |
((num & 3840) << 12);
return new Css.Color(num);
}
throw new Error("E_CSS_COLOR");
}

export class ParserHandler implements CssTokenizer.TokenizerHandler {
flavor: StylesheetFlavor;

Expand Down Expand Up @@ -1986,18 +1962,7 @@ export class Parser {
continue;
case Action.VAL_HASH:
num = parseInt(token.text, 16);
try {
valStack.push(colorFromHash(token.text));
} catch (err) {
if (this.actions === actionsPropVal && tokenizer.hasMark()) {
tokenizer.reset();
this.actions = actionsSelectorStart;
handler.startSelectorRule();
continue;
}
handler.error("E_CSS_COLOR", token);
this.actions = actionsError;
}
valStack.push(new Css.HexColor(token.text));
tokenizer.consume();
continue;
case Action.VAL_NUM:
Expand Down
37 changes: 31 additions & 6 deletions packages/core/src/vivliostyle/css-validator.ts
Expand Up @@ -352,6 +352,8 @@ export const ALLOW_SLASH = 2048;

export const ALLOW_URANGE = 4096;

export const ALLOW_IMAGE = 8192;

export type ValueMap = {
[key: string]: Css.Val;
};
Expand Down Expand Up @@ -431,6 +433,11 @@ export class PrimitiveValidator extends PropertyValidator {
if (this.allowed & ALLOW_IDENT) {
return ident;
}
if (this.allowed & ALLOW_COLOR) {
if (CSS.supports("color", ident.name)) {
return ident;
}
}
return null;
}

Expand Down Expand Up @@ -492,9 +499,11 @@ export class PrimitiveValidator extends PropertyValidator {
/**
* @override
*/
visitColor(color: Css.Color): Css.Val {
visitHexColor(color: Css.HexColor): Css.Val {
if (this.allowed & ALLOW_COLOR) {
return color;
if (/^([0-9A-F]{3,4}|([0-9A-F]{2}){3,4})$/i.test(color.hex)) {
return color;
}
}
return null;
}
Expand Down Expand Up @@ -537,6 +546,16 @@ export class PrimitiveValidator extends PropertyValidator {
* @override
*/
visitFunc(func: Css.Func): Css.Val {
if (this.allowed & ALLOW_COLOR) {
if (CSS.supports("color", func.toString())) {
return func;
}
}
if (this.allowed & ALLOW_IMAGE) {
if (CSS.supports("background-image", func.toString())) {
return func;
}
}
return null;
}

Expand Down Expand Up @@ -764,7 +783,7 @@ export class ListValidator extends PropertyValidator {
/**
* @override
*/
visitColor(color: Css.Color): Css.Val {
visitHexColor(color: Css.HexColor): Css.Val {
return this.validateSingle(color);
}

Expand Down Expand Up @@ -1152,7 +1171,7 @@ export class ShorthandValidator extends Css.Visitor {
/**
* @override
*/
visitColor(color: Css.Color): Css.Val {
visitHexColor(color: Css.HexColor): Css.Val {
return this.validateSingle(color);
}

Expand Down Expand Up @@ -1495,7 +1514,7 @@ export class ValidatorSet {
if (token.type == CssTokenizer.TokenType.NUMERIC) {
cssval = new Css.Numeric(token.num, token.text);
} else if (token.type == CssTokenizer.TokenType.HASH) {
cssval = CssParser.colorFromHash(token.text);
cssval = new Css.HexColor(token.text);
} else if (token.type == CssTokenizer.TokenType.IDENT) {
cssval = Css.getName(token.text);
} else {
Expand Down Expand Up @@ -1589,9 +1608,12 @@ export class ValidatorSet {
}

initBuiltInValidators(): void {
this.namedValidators["HASHCOLOR"] = this.primitive(
this.namedValidators["COLOR"] = this.primitive(
new PrimitiveValidator(ALLOW_COLOR, NO_IDENTS, NO_IDENTS),
);
this.namedValidators["IMAGE_FUNCTION"] = this.primitive(
new PrimitiveValidator(ALLOW_IMAGE, NO_IDENTS, NO_IDENTS),
);
this.namedValidators["POS_INT"] = this.primitive(
new PrimitiveValidator(ALLOW_POS_INT, NO_IDENTS, NO_IDENTS),
);
Expand Down Expand Up @@ -2106,6 +2128,9 @@ export class ValidatorSet {
: value.visit(validator);
if (rvalue) {
receiver.simpleProperty(name, rvalue, important);
} else if (!prefix && CSS.supports(name, value.toString())) {
receiver.simpleProperty(name, value, important);
return;
} else {
receiver.invalidPropertyValue(origName, value);
}
Expand Down
14 changes: 6 additions & 8 deletions packages/core/src/vivliostyle/css.ts
Expand Up @@ -59,7 +59,7 @@ export class Visitor {
return this.visitNum(num);
}

visitColor(color: Color): Val {
visitHexColor(color: HexColor): Val {
throw new Error("E_CSS_COLOR_NOT_ALLOWED");
}

Expand Down Expand Up @@ -156,7 +156,7 @@ export class FilterVisitor extends Visitor {
/**
* @override
*/
visitColor(color: Color): Val {
visitHexColor(color: HexColor): Val {
return color;
}

Expand Down Expand Up @@ -527,8 +527,8 @@ export class Int extends Num {
}
}

export class Color extends Val {
constructor(public rgb: number) {
export class HexColor extends Val {
constructor(public hex: string) {
super();
}

Expand All @@ -537,16 +537,14 @@ export class Color extends Val {
*/
appendTo(buf: Base.StringBuffer, toString: boolean): void {
buf.append("#");
const str = this.rgb.toString(16);
buf.append("000000".substr(str.length));
buf.append(str);
buf.append(this.hex);
}

/**
* @override
*/
visit(visitor: any): any {
return visitor.visitColor(this);
return visitor.visitHexColor(this);
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/vivliostyle/exprs.ts
Expand Up @@ -288,6 +288,7 @@ export const defaultUnitSizes: { [key: string]: number } = {
export function needUnitConversion(unit: string): boolean {
switch (unit) {
case "q":
return !CSS.supports("font-size", "1q");
case "rem":
return true;
default:
Expand Down

0 comments on commit 1d5f493

Please sign in to comment.