From 83eec2d24ad0479733c5c2c223a713b12d3830e2 Mon Sep 17 00:00:00 2001 From: Nick Badal Date: Sat, 21 May 2022 20:26:14 -0700 Subject: [PATCH] feat: add property parsing and ClipEffect for clip property This fixes reftests/clip --- src/css/index.ts | 8 ++++++ src/css/property-descriptors/clip.ts | 38 ++++++++++++++++++++++++++++ src/css/types/length-percentage.ts | 19 ++++++++------ src/render/bound-curves.ts | 25 +++++++++++++++++- src/render/stacking-context.ts | 13 +++++++++- tests/reftests/clip.html | 9 ++++--- 6 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 src/css/property-descriptors/clip.ts diff --git a/src/css/index.ts b/src/css/index.ts index b338871..49e7e29 100644 --- a/src/css/index.ts +++ b/src/css/index.ts @@ -30,6 +30,7 @@ import { borderRightWidth, borderTopWidth } from './property-descriptors/border-width'; +import {clip} from './property-descriptors/clip'; import {color} from './property-descriptors/color'; import {direction} from './property-descriptors/direction'; import {display, DISPLAY} from './property-descriptors/display'; @@ -107,6 +108,7 @@ export class CSSParsedDeclaration { borderBottomWidth: ReturnType; borderLeftWidth: ReturnType; boxShadow: ReturnType; + clip: ReturnType; color: Color; direction: ReturnType; display: ReturnType; @@ -225,6 +227,12 @@ export class CSSParsedDeclaration { this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth); this.wordBreak = parse(context, wordBreak, declaration.wordBreak); this.zIndex = parse(context, zIndex, declaration.zIndex); + + if (this.position == POSITION.ABSOLUTE || this.position == POSITION.FIXED) { + this.clip = parse(context, clip, declaration.clip); + } else { + this.clip = null; + } } isVisible(): boolean { diff --git a/src/css/property-descriptors/clip.ts b/src/css/property-descriptors/clip.ts new file mode 100644 index 0000000..275ed88 --- /dev/null +++ b/src/css/property-descriptors/clip.ts @@ -0,0 +1,38 @@ +import {IPropertyValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; +import {CSSValue, isDimensionToken, isIdentWithValue, nonFunctionArgSeparator} from '../syntax/parser'; +import {Context} from '../../core/context'; +import {DimensionToken, TokenType} from '../syntax/tokenizer'; + +export interface RectClip { + top: DimensionToken; + right: DimensionToken; + bottom: DimensionToken; + left: DimensionToken; +} + +export const clip: IPropertyValueDescriptor = { + name: 'clip', + initialValue: 'auto', + type: PropertyDescriptorParsingType.VALUE, + prefix: false, + parse: (_context: Context, token: CSSValue) => { + if (isIdentWithValue(token, 'auto')) { + return null; + } + if (token.type !== TokenType.FUNCTION || token.name !== 'rect') { + throw new Error('Clip value must be auto or a rect function'); + } + + const rectArgs = token.values.filter(nonFunctionArgSeparator).filter(isDimensionToken); + if (rectArgs.length !== 4) { + throw new Error('Rect clip must have 4 dimension elements'); + } + + return { + top: rectArgs[0], + right: rectArgs[1], + bottom: rectArgs[2], + left: rectArgs[3] + }; + } +}; diff --git a/src/css/types/length-percentage.ts b/src/css/types/length-percentage.ts index 138aacd..07b3b3f 100644 --- a/src/css/types/length-percentage.ts +++ b/src/css/types/length-percentage.ts @@ -40,15 +40,18 @@ export const getAbsoluteValue = (token: LengthPercentage, parent: number): numbe } if (isDimensionToken(token)) { - switch (token.unit) { - case 'rem': - case 'em': - return 16 * token.number; // TODO use correct font-size - case 'px': - default: - return token.number; - } + return getAbsoluteValueForDimension(token); } return token.number; }; +export const getAbsoluteValueForDimension = (token: DimensionToken): number => { + switch (token.unit) { + case 'rem': + case 'em': + return 16 * token.number; // TODO use correct font-size + case 'px': + default: + return token.number; + } +}; diff --git a/src/render/bound-curves.ts b/src/render/bound-curves.ts index f6d19d4..6a7b30c 100644 --- a/src/render/bound-curves.ts +++ b/src/render/bound-curves.ts @@ -1,5 +1,5 @@ import {ElementContainer} from '../dom/element-container'; -import {getAbsoluteValue, getAbsoluteValueForTuple} from '../css/types/length-percentage'; +import {getAbsoluteValue, getAbsoluteValueForTuple, getAbsoluteValueForDimension} from '../css/types/length-percentage'; import {Vector} from './vector'; import {BezierCurve} from './bezier-curve'; import {Path} from './path'; @@ -21,6 +21,10 @@ export class BoundCurves { readonly topRightBorderBox: Path; readonly bottomRightBorderBox: Path; readonly bottomLeftBorderBox: Path; + readonly topLeftClipBox: Path; + readonly topRightClipBox: Path; + readonly bottomRightClipBox: Path; + readonly bottomLeftClipBox: Path; readonly topLeftPaddingBox: Path; readonly topRightPaddingBox: Path; readonly bottomRightPaddingBox: Path; @@ -72,6 +76,17 @@ export class BoundCurves { const paddingBottom = getAbsoluteValue(styles.paddingBottom, element.bounds.width); const paddingLeft = getAbsoluteValue(styles.paddingLeft, element.bounds.width); + let rectClipTop = 0; + let rectClipRight = 0; + let rectClipBottom = 0; + let rectClipLeft = 0; + if (styles.clip) { + rectClipTop = getAbsoluteValueForDimension(styles.clip.top); + rectClipRight = getAbsoluteValueForDimension(styles.clip.right); + rectClipBottom = getAbsoluteValueForDimension(styles.clip.bottom); + rectClipLeft = getAbsoluteValueForDimension(styles.clip.left); + } + this.topLeftBorderDoubleOuterBox = tlh > 0 || tlv > 0 ? getCurvePoints( @@ -223,6 +238,10 @@ export class BoundCurves { blh > 0 || blv > 0 ? getCurvePoints(bounds.left, bounds.top + leftHeight, blh, blv, CORNER.BOTTOM_LEFT) : new Vector(bounds.left, bounds.top + bounds.height); + this.topLeftClipBox = new Vector(bounds.left + rectClipLeft, bounds.top + rectClipTop); + this.topRightClipBox = new Vector(bounds.left + rectClipRight, bounds.top + rectClipTop); + this.bottomRightClipBox = new Vector(bounds.left + rectClipRight, bounds.top + rectClipBottom); + this.bottomLeftClipBox = new Vector(bounds.left + rectClipLeft, bounds.top + rectClipBottom); this.topLeftPaddingBox = tlh > 0 || tlv > 0 ? getCurvePoints( @@ -369,6 +388,10 @@ export const calculateBorderBoxPath = (curves: BoundCurves): Path[] => { return [curves.topLeftBorderBox, curves.topRightBorderBox, curves.bottomRightBorderBox, curves.bottomLeftBorderBox]; }; +export const calculateRectClipPath = (curves: BoundCurves): Path[] => { + return [curves.topLeftClipBox, curves.topRightClipBox, curves.bottomRightClipBox, curves.bottomLeftClipBox]; +}; + export const calculateContentBoxPath = (curves: BoundCurves): Path[] => { return [ curves.topLeftContentBox, diff --git a/src/render/stacking-context.ts b/src/render/stacking-context.ts index c5ac088..a0fd484 100644 --- a/src/render/stacking-context.ts +++ b/src/render/stacking-context.ts @@ -1,6 +1,6 @@ import {ElementContainer, FLAGS} from '../dom/element-container'; import {contains} from '../core/bitwise'; -import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath} from './bound-curves'; +import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath, calculateRectClipPath} from './bound-curves'; import {ClipEffect, EffectTarget, IElementEffect, isClipEffect, OpacityEffect, TransformEffect} from './effects'; import {OVERFLOW} from '../css/property-descriptors/overflow'; import {equalPath} from './path'; @@ -61,6 +61,12 @@ export class ElementPaint { this.effects.push(new ClipEffect(paddingBox, EffectTarget.CONTENT)); } } + + if (this.container.styles.clip) { + const clipBox = calculateRectClipPath(this.curves); + const clipEffect = new ClipEffect(clipBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT); + this.effects.push(clipEffect); + } } getEffects(target: EffectTarget): IElementEffect[] { @@ -81,6 +87,11 @@ export class ElementPaint { ); } } + if (parent.container.styles.clip) { + const clipBox = calculateRectClipPath(parent.curves); + const clipEffect = new ClipEffect(clipBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT); + effects.unshift(clipEffect); + } } else { effects.unshift(...croplessEffects); } diff --git a/tests/reftests/clip.html b/tests/reftests/clip.html index 0ddeed0..88f4cae 100644 --- a/tests/reftests/clip.html +++ b/tests/reftests/clip.html @@ -1,5 +1,5 @@ - + Inline text in the top element @@ -16,7 +16,7 @@ border: 5px solid blue; } body { - font-family: Arial; + font-family: Arial, sans-serif; } @@ -37,10 +37,13 @@
Some inline text followed by text in span followed by more inline text.

Then a block level element.

Then more inline text.
-
Some inline text followed by text in span followed by more inline text.

Then a block level element.

Then more inline text.
+ +
Some inline text followed by text in span followed by more inline text. +

Then a block level element.

+ Then more inline text.