26 public $name =
'ExtractStyleBlocks';
35 $this->_tidy =
new csstidy();
36 $this->_tidy->set_cfg(
'lowercase_s',
false);
39 $this->_enum_attrdef =
new HTMLPurifier_AttrDef_Enum(array(
'first-child',
'link',
'visited',
'active',
'hover',
'focus'));
47 $this->_styleMatches[] = $matches[1];
54 public function preFilter($html, $config, $context) {
55 $tidy = $config->get(
'Filter.ExtractStyleBlocks.TidyImpl');
56 if ($tidy !== null) $this->_tidy = $tidy;
57 $html = preg_replace_callback(
'#<style(?:\s.*)?>(.+)</style>#isU', array($this,
'styleCallback'), $html);
59 $this->_styleMatches = array();
60 $context->register(
'StyleBlocks', $style_blocks);
62 foreach ($style_blocks as &$style) {
63 $style = $this->
cleanCSS($style, $config, $context);
77 public function cleanCSS($css, $config, $context) {
79 $scope = $config->get(
'Filter.ExtractStyleBlocks.Scope');
80 if ($scope !== null) {
81 $scopes = array_map(
'trim', explode(
',', $scope));
87 if (strncmp(
'<!--', $css, 4) === 0) {
88 $css = substr($css, 4);
90 if (strlen($css) > 3 && substr($css, -3) ==
'-->') {
91 $css = substr($css, 0, -3);
94 set_error_handler(
'htmlpurifier_filter_extractstyleblocks_muteerrorhandler');
95 $this->_tidy->parse($css);
96 restore_error_handler();
97 $css_definition = $config->getDefinition(
'CSS');
98 $html_definition = $config->getDefinition(
'HTML');
100 foreach ($this->_tidy->css as $k => $decls) {
102 $new_decls = array();
103 foreach ($decls as $selector => $style) {
104 $selector = trim($selector);
105 if ($selector ===
'')
continue;
168 $selectors = array_map(
'trim', explode(
',', $selector));
169 $new_selectors = array();
170 foreach ($selectors as $sel) {
172 $basic_selectors = preg_split(
'/\s*([+> ])\s*/', $sel, -1, PREG_SPLIT_DELIM_CAPTURE);
178 for ($i = 0, $c = count($basic_selectors); $i < $c; $i++) {
179 $x = $basic_selectors[$i];
185 $delim =
' ' . $x .
' ';
189 $components = preg_split(
'/([#.:])/', $x, -1, PREG_SPLIT_DELIM_CAPTURE);
192 for ($j = 0, $cc = count($components); $j < $cc; $j ++) {
193 $y = $components[$j];
195 if ($y ===
'*' || isset($html_definition->info[$y = strtolower($y)])) {
209 if ($sdelim ===
'#') {
211 } elseif ($sdelim ===
'.') {
213 } elseif ($sdelim ===
':') {
218 $r = $attrdef->validate($y, $config, $context);
231 if ($nsel === null) {
234 $nsel .= $delim . $nx;
242 if ($nsel !== null) {
243 if (!empty($scopes)) {
244 foreach ($scopes as $s) {
245 $new_selectors[] =
"$s $nsel";
248 $new_selectors[] = $nsel;
252 if (empty($new_selectors))
continue;
253 $selector = implode(
', ', $new_selectors);
254 foreach ($style as
$name => $value) {
255 if (!isset($css_definition->info[
$name])) {
256 unset($style[
$name]);
259 $def = $css_definition->info[
$name];
260 $ret = $def->validate($value, $config, $context);
261 if (
$ret ===
false) unset($style[
$name]);
264 $new_decls[$selector] = $style;
266 $new_css[$k] = $new_decls;
270 $this->_tidy->css = $new_css;
271 $this->_tidy->import = array();
272 $this->_tidy->charset = null;
273 $this->_tidy->namespace = null;
274 $css = $this->_tidy->print->plain();
277 if ($config->get(
'Filter.ExtractStyleBlocks.Escaping')) {
279 array(
'<',
'>',
'&'),
280 array(
'\3C ',
'\3E ',
'\26 '),