From a68b6afda1373bcdd9b2a90cf0d5fc57a799b0ad Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sat, 20 Jan 2007 01:40:56 +0000 Subject: [PATCH] [1.4.0] CSS property background-position implemented. Also: - Fixed some misinformation in Percentage - Add support for lowercase CSS length units git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@661 48356398-32a2-884e-a903-53898d9a118a --- NEWS | 7 +- .../AttrDef/BackgroundPosition.php | 130 ++++++++++++++++++ library/HTMLPurifier/AttrDef/CSSLength.php | 1 + library/HTMLPurifier/AttrDef/Percentage.php | 5 +- library/HTMLPurifier/CSSDefinition.php | 1 + .../AttrDef/BackgroundPositionTest.php | 71 ++++++++++ tests/HTMLPurifier/AttrDef/CSSLengthTest.php | 2 + tests/HTMLPurifier/AttrDef/CSSTest.php | 1 + tests/test_files.php | 1 + 9 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 library/HTMLPurifier/AttrDef/BackgroundPosition.php create mode 100644 tests/HTMLPurifier/AttrDef/BackgroundPositionTest.php diff --git a/NEWS b/NEWS index 11b3438e..476d7d37 100644 --- a/NEWS +++ b/NEWS @@ -11,9 +11,10 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier 1.4.0, unknown release date ! Implemented list-style-image, URIs now allowed in list-style -! Implemented background-image, background-repeat and background-attachment - CSS properties. background shorthand property HAS NOT been extended - to allow these, and background-position IS NOT implemented yet. +! Implemented background-image, background-repeat, background-attachment + and background-position CSS properties. Background shorthand property is + currently equivalent to background-image and does not support the + new properties. ! Configuration documentation looks nicer ! Added smoketest 'all.php', which loads all other smoketests via frames ! Added %Core.EscapeNonASCIICharacters to workaround loss of Unicode diff --git a/library/HTMLPurifier/AttrDef/BackgroundPosition.php b/library/HTMLPurifier/AttrDef/BackgroundPosition.php new file mode 100644 index 00000000..0c620b39 --- /dev/null +++ b/library/HTMLPurifier/AttrDef/BackgroundPosition.php @@ -0,0 +1,130 @@ + | | left | center | right + ] + [ + | | top | center | bottom + ]? + ] | + [ // this signifies that the vertical and horizontal adjectives + // can be arbitrarily ordered, however, there can only be two, + // one of each, or none at all + [ + left | center | right + ] || + [ + top | center | bottom + ] + ] + top, left = 0% + center, (none) = 50% + bottom, right = 100% +*/ + +/* QuirksMode says: + keyword + length/percentage must be ordered correctly, as per W3C + + Internet Explorer and Opera, however, support arbitrary ordering. We + should fix it up. + + Minor issue though, not strictly necessary. +*/ + +// control freaks may appreciate the ability to convert these to +// percentages or something, but it's not necessary + +/** + * Validates the value of background-position. + */ +class HTMLPurifier_AttrDef_BackgroundPosition extends HTMLPurifier_AttrDef +{ + + var $length; + var $percentage; + + function HTMLPurifier_AttrDef_BackgroundPosition() { + $this->length = new HTMLPurifier_AttrDef_CSSLength(); + $this->percentage = new HTMLPurifier_AttrDef_Percentage(); + } + + function validate($string, $config, &$context) { + $string = $this->parseCDATA($string); + $bits = explode(' ', $string); + + $keywords = array(); + $keywords['h'] = false; // left, right + $keywords['v'] = false; // top, bottom + $keywords['c'] = false; // center + $measures = array(); + + $i = 0; + + $lookup = array( + 'top' => 'v', + 'bottom' => 'v', + 'left' => 'h', + 'right' => 'h', + 'center' => 'c' + ); + + foreach ($bits as $bit) { + if ($bit === '') continue; + + // test for keyword + $lbit = ctype_lower($bit) ? $bit : strtolower($bit); + if (isset($lookup[$lbit])) { + $status = $lookup[$lbit]; + $keywords[$status] = $lbit; + $i++; + } + + // test for length + $r = $this->length->validate($bit, $config, &$context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + + // test for percentage + $r = $this->percentage->validate($bit, $config, &$context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + + } + + if (!$i) return false; // no valid values were caught + + + $ret = array(); + + // first keyword + if ($keywords['h']) $ret[] = $keywords['h']; + elseif (count($measures)) $ret[] = array_shift($measures); + elseif ($keywords['c']) { + $ret[] = $keywords['c']; + $keywords['c'] = false; // prevent re-use: center = center center + } + + if ($keywords['v']) $ret[] = $keywords['v']; + elseif (count($measures)) $ret[] = array_shift($measures); + elseif ($keywords['c']) $ret[] = $keywords['c']; + + if (empty($ret)) return false; + return implode(' ', $ret); + + } + +} + +?> \ No newline at end of file diff --git a/library/HTMLPurifier/AttrDef/CSSLength.php b/library/HTMLPurifier/AttrDef/CSSLength.php index b279eabf..50613a39 100644 --- a/library/HTMLPurifier/AttrDef/CSSLength.php +++ b/library/HTMLPurifier/AttrDef/CSSLength.php @@ -40,6 +40,7 @@ class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef // we assume all units are two characters $unit = substr($length, $strlen - 2); + if (!ctype_lower($unit)) $unit = strtolower($unit); $number = substr($length, 0, $strlen - 2); if (!isset($this->units[$unit])) return false; diff --git a/library/HTMLPurifier/AttrDef/Percentage.php b/library/HTMLPurifier/AttrDef/Percentage.php index 35fb5ab0..fcab2868 100644 --- a/library/HTMLPurifier/AttrDef/Percentage.php +++ b/library/HTMLPurifier/AttrDef/Percentage.php @@ -4,14 +4,13 @@ require_once 'HTMLPurifier/AttrDef.php'; require_once 'HTMLPurifier/AttrDef/Number.php'; /** - * Validates a Percentage as defined by the HTML spec. - * @note This also allows integer pixel values. + * Validates a Percentage as defined by the CSS spec. */ class HTMLPurifier_AttrDef_Percentage extends HTMLPurifier_AttrDef { /** - * Instance of HTMLPurifier_AttrDef_Number to defer pixel validation + * Instance of HTMLPurifier_AttrDef_Number to defer number validation */ var $number_def; diff --git a/library/HTMLPurifier/CSSDefinition.php b/library/HTMLPurifier/CSSDefinition.php index d2227e03..cb97b6c3 100644 --- a/library/HTMLPurifier/CSSDefinition.php +++ b/library/HTMLPurifier/CSSDefinition.php @@ -79,6 +79,7 @@ class HTMLPurifier_CSSDefinition $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( array('scroll', 'fixed') ); + $this->info['background-position'] = new HTMLPurifier_AttrDef_BackgroundPosition(); // pending its own validator as a shorthand $this->info['background'] = diff --git a/tests/HTMLPurifier/AttrDef/BackgroundPositionTest.php b/tests/HTMLPurifier/AttrDef/BackgroundPositionTest.php new file mode 100644 index 00000000..ce720841 --- /dev/null +++ b/tests/HTMLPurifier/AttrDef/BackgroundPositionTest.php @@ -0,0 +1,71 @@ +def = new HTMLPurifier_AttrDef_BackgroundPosition(); + + // explicitly cited in spec + $this->assertDef('0% 0%'); + $this->assertDef('100% 100%'); + $this->assertDef('14% 84%'); + $this->assertDef('2cm 1cm'); + $this->assertDef('top'); + $this->assertDef('left'); + $this->assertDef('center'); + $this->assertDef('right'); + $this->assertDef('bottom'); + $this->assertDef('left top'); + $this->assertDef('center top'); + $this->assertDef('right top'); + $this->assertDef('left center'); + $this->assertDef('right center'); + $this->assertDef('left bottom'); + $this->assertDef('center bottom'); + $this->assertDef('right bottom'); + + // reordered due to internal impl details + $this->assertDef('top left', 'left top'); + $this->assertDef('top center', 'center top'); + $this->assertDef('top right', 'right top'); + $this->assertDef('center left', 'left center'); + $this->assertDef('center center', 'center'); // two centers collide + $this->assertDef('center right', 'right center'); + $this->assertDef('bottom left', 'left bottom'); + $this->assertDef('bottom center', 'center bottom'); + $this->assertDef('bottom right', 'right bottom'); + + // more cases from the defined syntax + $this->assertDef('1.32in 4ex'); + $this->assertDef('-14% -84.65%'); + $this->assertDef('-1in -4ex'); + $this->assertDef('-1pc 2.3%'); + + // keyword mixing + $this->assertDef('3em top'); + $this->assertDef('left 50%'); + + // fixable keyword mixing + $this->assertDef('top 3em', '3em top'); + $this->assertDef('50% left', 'left 50%'); + + // whitespace collapsing + $this->assertDef('3em top', '3em top'); + $this->assertDef("left\n \t foo ", 'left'); + + // invalid uses (we're going to be strict on these) + $this->assertDef('foo bar', false); + $this->assertDef('left left', 'left'); + $this->assertDef('left right top bottom center left', 'left bottom'); + $this->assertDef('0fr 9%', '9%'); + + } + +} + +?> \ No newline at end of file diff --git a/tests/HTMLPurifier/AttrDef/CSSLengthTest.php b/tests/HTMLPurifier/AttrDef/CSSLengthTest.php index f591f5d5..fabea20f 100644 --- a/tests/HTMLPurifier/AttrDef/CSSLengthTest.php +++ b/tests/HTMLPurifier/AttrDef/CSSLengthTest.php @@ -22,6 +22,8 @@ class HTMLPurifier_AttrDef_CSSLengthTest extends HTMLPurifier_AttrDefHarness $this->assertDef('3pt'); $this->assertDef('3pc'); + $this->assertDef('3PX', '3px'); + $this->assertDef('3', false); $this->assertDef('3miles', false); diff --git a/tests/HTMLPurifier/AttrDef/CSSTest.php b/tests/HTMLPurifier/AttrDef/CSSTest.php index cb5e8083..e63153d0 100644 --- a/tests/HTMLPurifier/AttrDef/CSSTest.php +++ b/tests/HTMLPurifier/AttrDef/CSSTest.php @@ -78,6 +78,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness $this->assertDef('background-image:none;'); $this->assertDef('background-repeat:repeat-y;'); $this->assertDef('background-attachment:fixed;'); + $this->assertDef('background-position:left 90%;'); // duplicates $this->assertDef('text-align:right;text-align:left;', diff --git a/tests/test_files.php b/tests/test_files.php index f8b004cf..378458f5 100644 --- a/tests/test_files.php +++ b/tests/test_files.php @@ -49,6 +49,7 @@ $test_files[] = 'AttrDef/BorderTest.php'; $test_files[] = 'AttrDef/ListStyleTest.php'; $test_files[] = 'AttrDef/Email/SimpleCheckTest.php'; $test_files[] = 'AttrDef/CSSURITest.php'; +$test_files[] = 'AttrDef/BackgroundPositionTest.php'; $test_files[] = 'IDAccumulatorTest.php'; $test_files[] = 'TagTransformTest.php'; $test_files[] = 'AttrTransform/LangTest.php';