diff --git a/CHANGELOG b/CHANGELOG index 0546900f7..9b354a962 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -41,6 +41,7 @@ CHANGELOG Roundcube Webmail - Localized timezone selector (#4983) - Use 7bit encoding for ISO-2022-* charsets in sent mail (#5640) - Handle inline images also inside multipart/mixed messages (#5905) +- Fix css conflicts in user interface and e-mail content (#5891) - Fix duplicated signature when using Back button in Chrome (#5809) - Fix touch event issue on messages list in IE/Edge (#5781) - Fix so links over images are not removed in plain text signatures converted from HTML (#4473) diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index 679e2924c..128578012 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -373,10 +373,11 @@ class rcube_utils * @param string CSS source code * @param string Container ID to use as prefix * @param bool Allow remote content + * @param string Prefix to be added to id/class identifier * * @return string Modified CSS source */ - public static function mod_css_styles($source, $container_id, $allow_remote = false) + public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '') { $last_pos = 0; $replacements = new rcube_string_replacer; @@ -432,23 +433,37 @@ class rcube_utils $last_pos = $pos2 - ($length - strlen($repl)); } - // remove html comments and add #container to each tag selector. - // also replace body definition because we also stripped off the
tag - $source = preg_replace( - array( - '/(^\s*<\!--)|(-->\s*$)/m', - // (?!##str) below is to not match with ##str_replacement_0## - // from rcube_string_replacer used above, this is needed for - // cases like @media { body { position: fixed; } } (#5811) - '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*][a-z0-9\.\-_]*)/im', - '/'.preg_quote($container_id, '/').'\s+body/i', - ), - array( - '', - "\\1#$container_id \\2", - $container_id, - ), - $source); + // remove html comments + $source = preg_replace('/(^\s*<\!--)|(-->\s*$)/m', '', $source); + + // add #container to each tag selector and prefix to id/class identifiers + if ($container_id || $prefix) { + // (?!##str) below is to not match with ##str_replacement_0## + // from rcube_string_replacer used above, this is needed for + // cases like @media { body { position: fixed; } } (#5811) + $regexp = '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im'; + $callback = function($matches) use ($container_id, $prefix) { + $replace = $matches[2]; + + if ($prefix) { + $replace = str_replace(array('.', '#'), array(".$prefix", "#$prefix"), $replace); + } + + if ($container_id) { + $replace = "#$container_id " . $replace; + } + + return str_replace($matches[2], $replace, $matches[0]); + }; + + $source = preg_replace_callback($regexp, $callback, $source); + } + + // replace body definition because we also stripped off the tag + if ($container_id) { + $regexp = '/#' . preg_quote($container_id, '/') . '\s+body/i'; + $source = preg_replace($regexp, "#$container_id", $source); + } // put block contents back in $source = $replacements->resolve($source); diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php index 0880764a0..d45a88087 100644 --- a/program/lib/Roundcube/rcube_washtml.php +++ b/program/lib/Roundcube/rcube_washtml.php @@ -132,9 +132,9 @@ class rcube_washtml 'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border', 'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace', 'cellborder', 'size', 'lang', 'dir', 'usemap', 'shape', 'media', - 'background', 'src', 'poster', 'href', + 'background', 'src', 'poster', 'href', 'headers', // attributes of form elements - 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value', + 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value', 'for', // SVG 'accent-height', 'accumulate', 'additive', 'alignment-baseline', 'alphabetic', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseprofile', @@ -203,6 +203,9 @@ class rcube_washtml /* Allowed HTML attributes */ private $_html_attribs = array(); + /* A prefix to be added to id/class/for attribute values */ + private $_css_prefix; + /* Max nesting level */ private $max_nesting_level; @@ -218,6 +221,7 @@ class rcube_washtml $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); $this->_void_elements = array_flip((array)$p['void_elements']) + array_flip(self::$void_elements); + $this->_css_prefix = is_string($p['css_prefix']) && strlen($p['css_prefix']) ? $p['css_prefix'] : null; unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['void_elements']); @@ -338,6 +342,9 @@ class rcube_washtml $out = $value; } } + else if ($this->_css_prefix !== null && in_array($key, array('id', 'class', 'for'))) { + $out = preg_replace('/(\S+)/', $this->_css_prefix . '\1', $value); + } else if ($key) { $out = $value; } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index eb6afd327..5e302c97b 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -875,6 +875,8 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) 'charset' => RCUBE_CHARSET, 'cid_map' => $cid_replaces, 'html_elements' => array('body'), + 'css_prefix' => $p['css_prefix'], + 'container_id' => $p['container_id'], ); if (!$p['inline_html']) { @@ -886,10 +888,12 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) } // overwrite washer options with options from plugins - if (isset($p['html_elements'])) + if (isset($p['html_elements'])) { $wash_opts['html_elements'] = $p['html_elements']; - if (isset($p['html_attribs'])) + } + if (isset($p['html_attribs'])) { $wash_opts['html_attribs'] = $p['html_attribs']; + } // initialize HTML washer $washer = new rcube_washtml($wash_opts); @@ -908,8 +912,9 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) $washer->add_callback('a', 'rcmail_washtml_link_callback'); $washer->add_callback('area', 'rcmail_washtml_link_callback'); - if ($p['safe']) + if ($p['safe']) { $washer->add_callback('link', 'rcmail_washtml_link_callback'); + } } // Remove non-UTF8 characters (#1487813) @@ -1314,11 +1319,17 @@ function rcmail_message_body($attrib) $container_id = $container_class . (++$part_no); $container_attrib = array('class' => $container_class, 'id' => $container_id); - // Assign container ID to a global variable for use in rcmail_washtml_link_callback() - $GLOBALS['rcmail_html_container_id'] = $container_id; + $body_args = array( + 'safe' => $safe_mode, + 'plain' => !$RCMAIL->config->get('prefer_html'), + 'css_prefix' => 'v' . $part_no, + 'body_class' => 'rcmBody', + 'container_id' => $container_id, + 'container_attrib' => $container_attrib, + ); // Parse the part content for display - $body = rcmail_print_body($body, $part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html'))); + $body = rcmail_print_body($body, $part, $body_args); // check if the message body is PGP encrypted if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) { @@ -1326,7 +1337,7 @@ function rcmail_message_body($attrib) } if ($part->ctype_secondary == 'html') { - $body = rcmail_html4inline($body, $container_id, 'rcmBody', $container_attrib, $safe_mode); + $body = rcmail_html4inline($body, $body_args); } $out .= html::div($container_attrib, $plugin['prefix'] . $body); @@ -1469,22 +1480,22 @@ function rcmail_part_image_type($part) /** * Modify a HTML message that it can be displayed inside a HTML page */ -function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=array(), $allow_remote=false) +function rcmail_html4inline($body, $args) { - $last_style_pos = 0; - $cont_id = $container_id . ($body_class ? ' div.'.$body_class : ''); + $last_pos = 0; + $cont_id = $args['container_id'] . ($args['body_class'] ? ' div.' . $args['body_class'] : ''); // find STYLE tags - while (($pos = stripos($body, '', $pos))) { $pos = strpos($body, '>', $pos) + 1; $len = $pos2 - $pos; // replace all css definitions with #container [def] $styles = substr($body, $pos, $len); - $styles = rcube_utils::mod_css_styles($styles, $cont_id, $allow_remote); + $styles = rcube_utils::mod_css_styles($styles, $cont_id, $args['safe'], $args['css_prefix']); - $body = substr_replace($body, $styles, $pos, $len); - $last_style_pos = $pos2 + strlen($styles) - $len; + $body = substr_replace($body, $styles, $pos, $len); + $last_pos = $pos2 + strlen($styles) - $len; } $body = preg_replace(array( @@ -1511,13 +1522,13 @@ function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=a '', '<?', '?>', - '