Skip to content

Commit 2c88c46

Browse files
committed
Update Overflow 3 and add Overflow 4
Although the bug just requested an update of Overflow 3, a large number of properties were moved to Overflow 4, so it was necessary to implement Overflow 4 in order to maintain backwards compatibility. The tests are adapted from WPT. Bug: T368089 Change-Id: Ife9e7daa70adbaacffc84dd8ffcf347bc21afb42
1 parent 7ecc5a0 commit 2c88c46

File tree

4 files changed

+187
-15
lines changed

4 files changed

+187
-15
lines changed

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Update Display Level 3 to CR 2023-03-30
1010
* Add support for Ruby Level 1 (WD 2022-12-31)
1111
* Add support for Transforms Level 2 (WD 2021-11-09)
12+
* Update Overflow Level 3 to WD 2023-03-29 and add support for Overflow Level 4 (WD 2023-03-21)
1213

1314
## css-sanitizer 5.5.0 (2025-01-27)
1415
* Ensure <-token and identifiers are always separated as a security

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ The sanitizer recognizes the following CSS modules:
7777
* [Images Level 3, 2019-10-10](https://www.w3.org/TR/2019/CR-css-images-3-20191010)
7878
* [Masking Level 1, 2014-08-26](https://www.w3.org/TR/2014/CR-css-masking-1-20140826/)
7979
* [Multicol Level 1, 2019-10-15](https://www.w3.org/TR/2019/WD-css-multicol-1-20191015)
80-
* [Overflow Level 3, 2018-07-31](https://www.w3.org/TR/2018/WD-css-overflow-3-20180731)
80+
* [Overflow Level 3, 2023-03-29](https://www.w3.org/TR/2023/WD-css-overflow-3-20230329/)
81+
* [Overflow Level 4, 2023-03-21](https://www.w3.org/TR/2023/WD-css-overflow-4-20230321/)
8182
* [Page Level 3, 2018-10-18](https://www.w3.org/TR/2018/WD-css-page-3-20181018)
8283
* [Position Level 3, 2016-05-17](https://www.w3.org/TR/2016/WD-css-position-3-20160517/)
8384
* [Shapes Level 1, 2014-03-20](https://www.w3.org/TR/2014/CR-css-shapes-1-20140320/)

src/Sanitizer/StylePropertySanitizer.php

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function __construct( MatcherFactory $matcherFactory ) {
6060
$this->addKnownProperties( $this->cssImages3( $matcherFactory ) );
6161
$this->addKnownProperties( $this->cssFonts3( $matcherFactory ) );
6262
$this->addKnownProperties( $this->cssMulticol( $matcherFactory ) );
63-
$this->addKnownProperties( $this->cssOverflow3( $matcherFactory ) );
63+
$this->addKnownProperties( $this->cssOverflow4( $matcherFactory ) );
6464
$this->addKnownProperties( $this->cssUI4( $matcherFactory ) );
6565
$this->addKnownProperties( $this->cssCompositing1( $matcherFactory ) );
6666
$this->addKnownProperties( $this->cssWritingModes4( $matcherFactory ) );
@@ -656,7 +656,7 @@ protected function cssMulticol( MatcherFactory $matcherFactory ) {
656656

657657
/**
658658
* Properties for CSS Overflow Module Level 3
659-
* @see https://www.w3.org/TR/2018/WD-css-overflow-3-20180731/
659+
* @see https://www.w3.org/TR/2023/WD-css-overflow-3-20230329/
660660
* @param MatcherFactory $matcherFactory Factory for Matchers
661661
* @return Matcher[] Array mapping declaration names (lowercase) to Matchers for the values
662662
*/
@@ -675,24 +675,82 @@ protected function cssOverflow3( MatcherFactory $matcherFactory ) {
675675
$props['overflow-y'] = $overflow;
676676
$props['overflow-inline'] = $overflow;
677677
$props['overflow-block'] = $overflow;
678+
$props['overflow-clip-margin'] = UnorderedGroup::someOf( [
679+
new KeywordMatcher( [ 'content-box', 'padding-box', 'border-box' ] ),
680+
$matcherFactory->length()
681+
] );
678682

679683
$props['text-overflow'] = new KeywordMatcher( [ 'clip', 'ellipsis' ] );
680-
$props['block-overflow'] = new Alternative( [
681-
new KeywordMatcher( [ 'clip', 'ellipsis' ] ),
682-
$matcherFactory->string(),
684+
685+
$props['scroll-behavior'] = new KeywordMatcher( [ 'auto', 'smooth' ] );
686+
$props['scrollbar-gutter'] = new Alternative( [
687+
new KeywordMatcher( 'auto' ),
688+
UnorderedGroup::allOf( [
689+
new KeywordMatcher( 'stable' ),
690+
Quantifier::optional( new KeywordMatcher( 'both-edges' ) )
691+
] )
683692
] );
684693

694+
$this->cache[__METHOD__] = $props;
695+
return $props;
696+
}
697+
698+
/**
699+
* Properties for CSS Overflow Module Level 4
700+
* @see https://www.w3.org/TR/2023/WD-css-overflow-4-20230321/
701+
* @param MatcherFactory $matcherFactory Factory for Matchers
702+
* @return Matcher[] Array mapping declaration names (lowercase) to Matchers for the values
703+
*/
704+
protected function cssOverflow4( MatcherFactory $matcherFactory ) {
705+
// @codeCoverageIgnoreStart
706+
if ( isset( $this->cache[__METHOD__] ) ) {
707+
return $this->cache[__METHOD__];
708+
}
709+
// @codeCoverageIgnoreEnd
710+
$props = $this->cssOverflow3( $matcherFactory );
711+
$props['-webkit-line-clamp'] = new Alternative( [
712+
new KeywordMatcher( 'none' ),
713+
$matcherFactory->integer()
714+
] );
715+
$props['block-ellipsis'] = new Alternative( [
716+
new KeywordMatcher( [ 'none', 'auto' ] ),
717+
$matcherFactory->string()
718+
] );
719+
$props['continue'] = new KeywordMatcher( [ 'auto', 'discard' ] );
685720
$props['line-clamp'] = new Alternative( [
686721
new KeywordMatcher( 'none' ),
687722
new Juxtaposition( [
688723
$matcherFactory->integer(),
689-
Quantifier::optional( $props['block-overflow'] ),
724+
Quantifier::optional( $props['block-ellipsis'] ),
690725
] ),
691726
] );
692727
$props['max-lines'] = new Alternative( [
693728
new KeywordMatcher( 'none' ), $matcherFactory->integer()
694729
] );
695-
$props['continue'] = new KeywordMatcher( [ 'auto', 'discard' ] );
730+
$clipMargin = $props['overflow-clip-margin'];
731+
$props['overflow-clip-margin-block'] = $clipMargin;
732+
$props['overflow-clip-margin-block-end'] = $clipMargin;
733+
$props['overflow-clip-margin-block-start'] = $clipMargin;
734+
$props['overflow-clip-margin-bottom'] = $clipMargin;
735+
$props['overflow-clip-margin-inline'] = $clipMargin;
736+
$props['overflow-clip-margin-inline-end'] = $clipMargin;
737+
$props['overflow-clip-margin-inline-start'] = $clipMargin;
738+
$props['overflow-clip-margin-inline-left'] = $clipMargin;
739+
$props['overflow-clip-margin-left'] = $clipMargin;
740+
$props['overflow-clip-margin-right'] = $clipMargin;
741+
$props['overflow-clip-margin-top'] = $clipMargin;
742+
$props['text-overflow'] = Quantifier::count(
743+
new Alternative( [
744+
new KeywordMatcher( [ 'clip', 'ellipsis' ] ),
745+
$matcherFactory->string(),
746+
new KeywordMatcher( 'fade' ),
747+
new FunctionMatcher(
748+
'fade',
749+
new Alternative( [ $matcherFactory->length(), $matcherFactory->percentage() ] )
750+
)
751+
] ),
752+
1, 2
753+
);
696754

697755
$this->cache[__METHOD__] = $props;
698756
return $props;

tests/Sanitizer/StylePropertySanitizerTest.php

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,127 @@ public static function provideDeclarations() {
244244
[ 'overflow-inline: scroll' ],
245245
[ 'overflow-block: scroll' ],
246246
[ 'text-overflow: ellipsis' ],
247-
[ 'block-overflow: ellipsis' ],
248-
[ 'block-overflow: "foo"' ],
249-
[ 'line-clamp: none' ],
250-
[ 'line-clamp: 123' ],
251-
[ 'line-clamp: 123 clip' ],
252-
[ 'line-clamp: 123 "foo"' ],
247+
248+
// cssOverflow4
253249
[ 'line-clamp: none "foo"', 'bad-value-for-property' ],
254-
[ 'max-lines: 1000' ],
250+
[ 'block-ellipsis: hidden', 'bad-value-for-property' ],
251+
[ 'block-ellipsis: none auto', 'bad-value-for-property' ],
252+
[ 'block-ellipsis: auto "string"', 'bad-value-for-property' ],
253+
[ 'block-ellipsis: "string" none', 'bad-value-for-property' ],
254+
[ 'block-ellipsis: "first" "second"', 'bad-value-for-property' ],
255+
[ 'block-ellipsis: none' ],
256+
[ 'block-ellipsis: auto' ],
257+
[ 'block-ellipsis: " etc., etc. "' ],
258+
[ 'continue: none', 'bad-value-for-property' ],
259+
[ 'continue: auto discard', 'bad-value-for-property' ],
260+
[ 'continue: auto' ],
255261
[ 'continue: discard' ],
262+
[ 'line-clamp: auto', 'bad-value-for-property' ],
263+
[ 'line-clamp: " etc., etc. "', 'bad-value-for-property' ],
264+
[ 'line-clamp: none 2', 'bad-value-for-property' ],
265+
[ 'line-clamp: auto 4', 'bad-value-for-property' ],
266+
[ 'line-clamp: none' ],
267+
[ 'line-clamp: 1' ],
268+
[ 'line-clamp: 6' ],
269+
[ 'line-clamp: 7 none' ],
270+
[ 'line-clamp: 8 auto' ],
271+
[ 'line-clamp: 9 " etc., etc. "' ],
272+
[ 'max-lines: auto', 'bad-value-for-property' ],
273+
[ 'max-lines: none none', 'bad-value-for-property' ],
274+
[ 'max-lines: 1 none', 'bad-value-for-property' ],
275+
[ 'max-lines: none 2', 'bad-value-for-property' ],
276+
[ 'max-lines: 3 4', 'bad-value-for-property' ],
277+
[ 'max-lines: none' ],
278+
[ 'max-lines: 1' ],
279+
[ 'max-lines: 6' ],
280+
[ 'overflow-clip-margin: 10px' ],
281+
[ 'overflow-clip-margin: content-box' ],
282+
[ 'overflow-clip-margin: content-box 10px' ],
283+
[ 'overflow-clip-margin: 10px content-box' ],
284+
[ 'overflow-clip-margin: 0px content-box' ],
285+
[ 'overflow-clip-margin: padding-box' ],
286+
[ 'overflow-clip-margin: padding-box 0px' ],
287+
[ 'overflow-clip-margin: padding-box 10px' ],
288+
[ 'overflow-clip-margin: 10px padding-box' ],
289+
[ 'overflow-clip-margin: border-box' ],
290+
[ 'overflow-clip-margin: border-box 0px' ],
291+
[ 'overflow-clip-margin: border-box 10px' ],
292+
[ 'overflow-clip-margin: 10px border-box' ],
293+
[ 'overflow-clip-margin: margin-box', 'bad-value-for-property' ],
294+
[ 'overflow-clip-margin: inset(10px)', 'bad-value-for-property' ],
295+
[ 'overflow: none', 'bad-value-for-property' ],
296+
[ 'overflow: visible clip auto', 'bad-value-for-property' ],
297+
[ 'overflow-x: visible clip', 'bad-value-for-property' ],
298+
[ 'overflow-y: clip hidden', 'bad-value-for-property' ],
299+
[ 'overflow-block: hidden scroll', 'bad-value-for-property' ],
300+
[ 'overflow-inline: scroll auto', 'bad-value-for-property' ],
301+
[ 'overflow: visible' ],
302+
[ 'overflow: hidden' ],
303+
[ 'overflow: clip' ],
304+
[ 'overflow: scroll' ],
305+
[ 'overflow: auto' ],
306+
[ 'overflow: visible visible' ],
307+
[ 'overflow: hidden visible' ],
308+
[ 'overflow: clip clip' ],
309+
[ 'overflow: scroll auto' ],
310+
[ 'overflow: auto auto' ],
311+
[ 'overflow-x: visible' ],
312+
[ 'overflow-x: scroll' ],
313+
[ 'overflow-y: clip' ],
314+
[ 'overflow-y: auto' ],
315+
[ 'overflow-block: hidden' ],
316+
[ 'overflow-block: clip' ],
317+
[ 'overflow-inline: scroll' ],
318+
[ 'overflow-inline: visible' ],
319+
[ 'scrollbar-gutter: auto both', 'bad-value-for-property' ],
320+
[ 'scrollbar-gutter: force auto', 'bad-value-for-property' ],
321+
[ 'scrollbar-gutter: auto always', 'bad-value-for-property' ],
322+
[ 'scrollbar-gutter: always stable', 'bad-value-for-property' ],
323+
[ 'scrollbar-gutter: force', 'bad-value-for-property' ],
324+
[ 'scrollbar-gutter: both', 'bad-value-for-property' ],
325+
[ 'scrollbar-gutter: force both', 'bad-value-for-property' ],
326+
[ 'scrollbar-gutter: 0', 'bad-value-for-property' ],
327+
[ 'scrollbar-gutter: 1px', 'bad-value-for-property' ],
328+
[ 'scrollbar-gutter: 3em', 'bad-value-for-property' ],
329+
[ 'scrollbar-gutter: 1 2 3', 'bad-value-for-property' ],
330+
[ 'scrollbar-gutter: none', 'bad-value-for-property' ],
331+
[ 'scrollbar-gutter: red', 'bad-value-for-property' ],
332+
[ 'scrollbar-gutter: stable both', 'bad-value-for-property' ],
333+
[ 'scrollbar-gutter: stable force', 'bad-value-for-property' ],
334+
[ 'scrollbar-gutter: stable both force', 'bad-value-for-property' ],
335+
[ 'scrollbar-gutter: always', 'bad-value-for-property' ],
336+
[ 'scrollbar-gutter: always both', 'bad-value-for-property' ],
337+
[ 'scrollbar-gutter: always force', 'bad-value-for-property' ],
338+
[ 'scrollbar-gutter: always both force', 'bad-value-for-property' ],
339+
[ 'scrollbar-gutter: auto stable', 'bad-value-for-property' ],
340+
[ 'scrollbar-gutter: auto both-edges', 'bad-value-for-property' ],
341+
[ 'scrollbar-gutter: both-edges', 'bad-value-for-property' ],
342+
[ 'scrollbar-gutter: both-edges auto', 'bad-value-for-property' ],
343+
[ 'scrollbar-gutter: stable auto', 'bad-value-for-property' ],
344+
[ 'scrollbar-gutter: auto' ],
345+
[ 'scrollbar-gutter: stable' ],
346+
[ 'scrollbar-gutter: stable both-edges' ],
347+
[ 'scrollbar-gutter: both-edges stable' ],
348+
[ 'text-overflow: auto', 'bad-value-for-property' ],
349+
[ 'text-overflow: clip ellipsis clip', 'bad-value-for-property' ],
350+
[ 'text-overflow: clip' ],
351+
[ 'text-overflow: ellipsis' ],
352+
[ '-webkit-line-clamp: auto', 'bad-value-for-property' ],
353+
[ '-webkit-line-clamp: none "~"', 'bad-value-for-property' ],
354+
[ '-webkit-line-clamp: 1 "~"', 'bad-value-for-property' ],
355+
[ '-webkit-line-clamp: none' ],
356+
[ '-webkit-line-clamp: 1' ],
357+
[ '-webkit-line-clamp: 6' ],
358+
359+
// No range restrictions
360+
// [ 'line-clamp: 0', 'bad-value-for-property' ],
361+
// [ 'line-clamp: -5', 'bad-value-for-property' ],
362+
// [ 'max-lines: 0', 'bad-value-for-property' ],
363+
// [ 'max-lines: -5', 'bad-value-for-property' ],
364+
// [ '-webkit-line-clamp: 0', 'bad-value-for-property' ],
365+
// [ '-webkit-line-clamp: -5', 'bad-value-for-property' ],
366+
// Why would this fail?
367+
// [ 'line-clamp: 3 none', 'bad-value-for-property' ],
256368

257369
// cssUI4
258370
[ 'outline-width: 1px' ],

0 commit comments

Comments
 (0)