diff --git a/core/includes/mail.inc b/core/includes/mail.inc index 7b08ad3376bcb4e3064376d2b14422aacaf03e98..0209bd501f74af9ab934531b37b0716ce83f0eae 100644 --- a/core/includes/mail.inc +++ b/core/includes/mail.inc @@ -5,6 +5,9 @@ * API functions for processing and sending e-mail. */ +use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\Xss; + /** * Composes and optionally sends an e-mail message. * @@ -288,7 +291,7 @@ function drupal_html_to_text($string, $allowed_tags = NULL) { $allowed_tags = isset($allowed_tags) ? array_intersect($supported_tags, $allowed_tags) : $supported_tags; // Make sure tags, entities and attributes are well-formed and properly nested. - $string = _filter_htmlcorrector(filter_xss($string, $allowed_tags)); + $string = Html::normalize(Xss::filter($string, $allowed_tags)); // Apply inline styles. $string = preg_replace('!</?(em|i)((?> +)[^>]*)?>!i', '/', $string); diff --git a/core/lib/Drupal/Component/Utility/Html.php b/core/lib/Drupal/Component/Utility/Html.php new file mode 100644 index 0000000000000000000000000000000000000000..bc0a91621433fb10589ff125854248cb2877e254 --- /dev/null +++ b/core/lib/Drupal/Component/Utility/Html.php @@ -0,0 +1,137 @@ +<?php + +/** + * @file + * Contains \Drupal\Component\Utility\Html. + */ + +namespace Drupal\Component\Utility; + +/** + * Provides DOMDocument helpers for parsing and serializing HTML strings. + */ +class Html { + + /** + * Normalizes an HTML snippet. + * + * This function is essentially \DOMDocument::normalizeDocument(), but + * operates on an HTML string instead of a \DOMDocument. + * + * @param string $html + * The HTML string to normalize. + * + * @return string + * The normalized HTML string. + */ + public static function normalize($html) { + $document = static::load($html); + return static::serialize($document); + } + + /** + * Parses an HTML snippet and returns it as a DOM object. + * + * This function loads the body part of a partial (X)HTML document and returns + * a full \DOMDocument object that represents this document. + * + * Use \Drupal\Component\Utility\Html::serialize() to serialize this + * \DOMDocument back to a string. + * + * @param string $html + * The partial (X)HTML snippet to load. Invalid markup will be corrected on + * import. + * + * @return \DOMDocument + * A \DOMDocument that represents the loaded (X)HTML snippet. + */ + public static function load($html) { + $document = <<<EOD +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head> +<body>!html</body> +</html> +EOD; + // PHP's \DOMDocument serialization adds straw whitespace in case the markup + // of the wrapping document contains newlines, so ensure to remove all + // newlines before injecting the actual HTML body to process. + $document = strtr($document, array("\n" => '', '!html' => $html)); + + $dom = new \DOMDocument(); + // Ignore warnings during HTML soup loading. + @$dom->loadHTML($document); + + return $dom; + } + + /** + * Converts the body of a \DOMDocument back to an HTML snippet. + * + * The function serializes the body part of a \DOMDocument back to an (X)HTML + * snippet. The resulting (X)HTML snippet will be properly formatted to be + * compatible with HTML user agents. + * + * @param \DOMDocument $document + * A \DOMDocument object to serialize, only the tags below the first <body> + * node will be converted. + * + * @return string + * A valid (X)HTML snippet, as a string. + */ + public static function serialize(\DOMDocument $document) { + $body_node = $document->getElementsByTagName('body')->item(0); + $html = ''; + + foreach ($body_node->getElementsByTagName('script') as $node) { + static::escapeCdataElement($node); + } + foreach ($body_node->getElementsByTagName('style') as $node) { + static::escapeCdataElement($node, '/*', '*/'); + } + foreach ($body_node->childNodes as $node) { + $html .= $document->saveXML($node); + } + return $html; + } + + /** + * Adds comments around a <!CDATA section in a \DOMNode. + * + * \DOMDocument::loadHTML() in \Drupal\Component\Utility\Html::load() makes + * CDATA sections from the contents of inline script and style tags. This can + * cause HTML4 browsers to throw exceptions. + * + * This function attempts to solve the problem by creating a + * \DOMDocumentFragment to comment the CDATA tag. + * + * @param \DOMNode $node + * The element potentially containing a CDATA node. + * @param string $comment_start + * (optional) A string to use as a comment start marker to escape the CDATA + * declaration. Defaults to '//'. + * @param string $comment_end + * (optional) A string to use as a comment end marker to escape the CDATA + * declaration. Defaults to an empty string. + */ + public static function escapeCdataElement(\DOMNode $node, $comment_start = '//', $comment_end = '') { + foreach ($node->childNodes as $child_node) { + if ($child_node instanceof \DOMCdataSection) { + $embed_prefix = "\n<!--{$comment_start}--><![CDATA[{$comment_start} ><!--{$comment_end}\n"; + $embed_suffix = "\n{$comment_start}--><!]]>{$comment_end}\n"; + + // Prevent invalid cdata escaping as this would throw a DOM error. + // This is the same behavior as found in libxml2. + // Related W3C standard: http://www.w3.org/TR/REC-xml/#dt-cdsection + // Fix explanation: http://en.wikipedia.org/wiki/CDATA#Nesting + $data = str_replace(']]>', ']]]]><![CDATA[>', $child_node->data); + + $fragment = $node->ownerDocument->createDocumentFragment(); + $fragment->appendXML($embed_prefix . $data . $embed_suffix); + $node->appendChild($fragment); + $node->removeChild($child_node); + } + } + } + +} diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module index a0bf85ea1f955dd7592bd3fc46e34e8b9c6a0750..544ff2e32afc6204635dc70a46195134e0b65715 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -5,6 +5,7 @@ * Adds bindings for client-side "text editors" to text formats. */ +use Drupal\Component\Utility\Html; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\editor\Entity\Editor; use Drupal\Component\Utility\NestedArray; @@ -661,7 +662,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) { * An array of all found UUIDs. */ function _editor_parse_file_uuids($text) { - $dom = filter_dom_load($text); + $dom = Html::load($text); $xpath = new \DOMXPath($dom); $uuids = array(); foreach ($xpath->query('//*[@data-editor-file-uuid]') as $node) { diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 6a8b941125447bc85e32b34cf043f41a03e657ba..2dd3469c61eea2e409546f7d34688e225a5cd002 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -4,6 +4,8 @@ * Attach custom data fields to Drupal entities. */ +use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\Xss; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Template\Attribute; use Drupal\entity\Entity\EntityViewDisplay; @@ -304,7 +306,7 @@ function field_cache_clear() { * UTF-8. */ function field_filter_xss($string) { - return _filter_htmlcorrector(filter_xss($string, _field_filter_xss_allowed_tags())); + return Html::normalize(Xss::filter($string, _field_filter_xss_allowed_tags())); } /** diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index 3252cecf78f971d4032bb94dd4e7c856589b78c3..8a643322f96715f158f53ef9db4a90e60c1fbb1b 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -5,6 +5,7 @@ * Field module functionality for the File module. */ +use Drupal\Component\Utility\Html; use Drupal\field\FieldInterface; /** @@ -181,8 +182,8 @@ function theme_file_upload_help($variables) { $descriptions = array(); - if (strlen($description)) { - $descriptions[] = _filter_htmlcorrector($description); + if (!empty($description)) { + $descriptions[] = Html::normalize($description); } if (isset($cardinality)) { if ($cardinality == -1) { diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 38af864a61372b4e4d01d540575cfe7ce55e9cac..e9c04d43fb349910fcfca8426ac94c9f08fd0818 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -5,6 +5,7 @@ * Framework for handling the filtering of content. */ +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\String; use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; @@ -693,103 +694,6 @@ function _filter_tips($format_id, $long = FALSE) { return $tips; } -/** - * Parses an HTML snippet and returns it as a DOM object. - * - * This function loads the body part of a partial (X)HTML document and returns - * a full DOMDocument object that represents this document. You can use - * filter_dom_serialize() to serialize this DOMDocument back to a XHTML - * snippet. - * - * @param $text - * The partial (X)HTML snippet to load. Invalid markup will be corrected on - * import. - * - * @return - * A DOMDocument that represents the loaded (X)HTML snippet. - */ -function filter_dom_load($text) { - $dom_document = new DOMDocument(); - // Ignore warnings during HTML soup loading. - @$dom_document->loadHTML('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>' . $text . '</body></html>'); - - return $dom_document; -} - -/** - * Converts a DOM object back to an HTML snippet. - * - * The function serializes the body part of a DOMDocument back to an XHTML - * snippet. The resulting XHTML snippet will be properly formatted to be - * compatible with HTML user agents. - * - * @param $dom_document - * A DOMDocument object to serialize, only the tags below - * the first <body> node will be converted. - * - * @return - * A valid (X)HTML snippet, as a string. - */ -function filter_dom_serialize($dom_document) { - $body_node = $dom_document->getElementsByTagName('body')->item(0); - $body_content = ''; - - foreach ($body_node->getElementsByTagName('script') as $node) { - filter_dom_serialize_escape_cdata_element($dom_document, $node); - } - - foreach ($body_node->getElementsByTagName('style') as $node) { - filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/'); - } - - foreach ($body_node->childNodes as $child_node) { - $body_content .= $dom_document->saveXML($child_node); - } - return $body_content; -} - -/** - * Adds comments around the <!CDATA section in a dom element. - * - * DOMDocument::loadHTML in filter_dom_load() makes CDATA sections from the - * contents of inline script and style tags. This can cause HTML 4 browsers to - * throw exceptions. - * - * This function attempts to solve the problem by creating a DocumentFragment - * and imitating the behavior in drupal_get_js(), commenting the CDATA tag. - * - * @param $dom_document - * The DOMDocument containing the $dom_element. - * @param $dom_element - * The element potentially containing a CDATA node. - * @param $comment_start - * (optional) A string to use as a comment start marker to escape the CDATA - * declaration. Defaults to '//'. - * @param $comment_end - * (optional) A string to use as a comment end marker to escape the CDATA - * declaration. Defaults to an empty string. - */ -function filter_dom_serialize_escape_cdata_element($dom_document, $dom_element, $comment_start = '//', $comment_end = '') { - foreach ($dom_element->childNodes as $node) { - if (get_class($node) == 'DOMCdataSection') { - // See drupal_get_js(). This code is more or less duplicated there. - $embed_prefix = "\n<!--{$comment_start}--><![CDATA[{$comment_start} ><!--{$comment_end}\n"; - $embed_suffix = "\n{$comment_start}--><!]]>{$comment_end}\n"; - - // Prevent invalid cdata escaping as this would throw a DOM error. - // This is the same behavior as found in libxml2. - // Related W3C standard: http://www.w3.org/TR/REC-xml/#dt-cdsection - // Fix explanation: http://en.wikipedia.org/wiki/CDATA#Nesting - $data = str_replace(']]>', ']]]]><![CDATA[>', $node->data); - - $fragment = $dom_document->createDocumentFragment(); - $fragment->appendXML($embed_prefix . $data . $embed_suffix); - $dom_element->appendChild($fragment); - $dom_element->removeChild($node); - } - } -} - /** * Prepares variables for text format guideline templates. * @@ -878,12 +782,12 @@ function _filter_html($text, $filter) { $text = filter_xss($text, $allowed_tags); if ($filter->settings['filter_html_nofollow']) { - $html_dom = filter_dom_load($text); + $html_dom = Html::load($text); $links = $html_dom->getElementsByTagName('a'); foreach ($links as $link) { $link->setAttribute('rel', 'nofollow'); } - $text = filter_dom_serialize($html_dom); + $text = Html::serialize($html_dom); } return trim($text); @@ -1127,13 +1031,6 @@ function _filter_url_trim($text, $length = NULL) { return $text; } -/** - * Scans the input and makes sure that HTML tags are properly closed. - */ -function _filter_htmlcorrector($text) { - return filter_dom_serialize(filter_dom_load($text)); -} - /** * Converts line breaks into <p> and <br> in an intelligent fashion. * @@ -1219,7 +1116,7 @@ function _filter_html_image_secure_process($text) { // Find the directory on the server where index.php resides. $local_dir = DRUPAL_ROOT . '/'; - $html_dom = filter_dom_load($text); + $html_dom = Html::load($text); $images = $html_dom->getElementsByTagName('img'); foreach ($images as $image) { $src = $image->getAttribute('src'); @@ -1245,7 +1142,7 @@ function _filter_html_image_secure_process($text) { // indicator. See filter_filter_secure_image_alter(). \Drupal::moduleHandler()->alter('filter_secure_image', $image); } - $text = filter_dom_serialize($html_dom); + $text = Html::serialize($html_dom); return $text; } diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php index 5b5ce632edcc6880ecece728a7e1f4b5d385d2b0..51cb70a37b62f5a0440f283cf731a3206f134e0f 100644 --- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php +++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php @@ -7,6 +7,7 @@ namespace Drupal\filter\Plugin\Filter; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\Xss; @@ -30,7 +31,7 @@ class FilterCaption extends FilterBase { public function process($text, $langcode, $cache, $cache_id) { if (stristr($text, 'data-caption') !== FALSE || stristr($text, 'data-align') !== FALSE) { - $dom = filter_dom_load($text); + $dom = Html::load($text); $xpath = new \DOMXPath($dom); foreach ($xpath->query('//*[@data-caption or @data-align]') as $node) { $caption = NULL; @@ -82,7 +83,7 @@ public function process($text, $langcode, $cache, $cache_id) { $altered_html = drupal_render($filter_caption); // Load the altered HTML into a new DOMDocument and retrieve the element. - $updated_node = filter_dom_load($altered_html)->getElementsByTagName('body') + $updated_node = Html::load($altered_html)->getElementsByTagName('body') ->item(0) ->childNodes ->item(0); @@ -94,7 +95,7 @@ public function process($text, $langcode, $cache, $cache_id) { $node->parentNode->replaceChild($updated_node, $node); } - return filter_dom_serialize($dom); + return Html::serialize($dom); } return $text; diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php index 8d9b8e0abbbbc756ca3e2acfe6aa6b708bd58f46..bc0a036f51ccf3458be22e321d08c607ce6d9133 100644 --- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php +++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php @@ -7,6 +7,7 @@ namespace Drupal\filter\Plugin\Filter; +use Drupal\Component\Utility\Html; use Drupal\filter\Plugin\FilterBase; /** @@ -25,7 +26,7 @@ class FilterHtmlCorrector extends FilterBase { * {@inheritdoc} */ public function process($text, $langcode, $cache, $cache_id) { - return _filter_htmlcorrector($text); + return Html::normalize($text); } } diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php index f686cee49ff979442d8892bdebbe5f1406bd5b78..a51d6b9bd1c72a8f337d860435448e522831ebe0 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php @@ -7,6 +7,7 @@ namespace Drupal\filter\Tests; +use Drupal\Component\Utility\Html; use Drupal\simpletest\DrupalUnitTestBase; use Drupal\filter\FilterBag; @@ -741,117 +742,117 @@ function testUrlFilterContent() { */ function testHtmlCorrectorFilter() { // Tag closing. - $f = _filter_htmlcorrector('<p>text'); + $f = Html::normalize('<p>text'); $this->assertEqual($f, '<p>text</p>', 'HTML corrector -- tag closing at the end of input.'); - $f = _filter_htmlcorrector('<p>text<p><p>text'); + $f = Html::normalize('<p>text<p><p>text'); $this->assertEqual($f, '<p>text</p><p></p><p>text</p>', 'HTML corrector -- tag closing.'); - $f = _filter_htmlcorrector("<ul><li>e1<li>e2"); + $f = Html::normalize("<ul><li>e1<li>e2"); $this->assertEqual($f, "<ul><li>e1</li><li>e2</li></ul>", 'HTML corrector -- unclosed list tags.'); - $f = _filter_htmlcorrector('<div id="d">content'); + $f = Html::normalize('<div id="d">content'); $this->assertEqual($f, '<div id="d">content</div>', 'HTML corrector -- unclosed tag with attribute.'); // XHTML slash for empty elements. - $f = _filter_htmlcorrector('<hr><br>'); + $f = Html::normalize('<hr><br>'); $this->assertEqual($f, '<hr /><br />', 'HTML corrector -- XHTML closing slash.'); - $f = _filter_htmlcorrector('<P>test</P>'); + $f = Html::normalize('<P>test</P>'); $this->assertEqual($f, '<p>test</p>', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); - $f = _filter_htmlcorrector('<P>test</p>'); + $f = Html::normalize('<P>test</p>'); $this->assertEqual($f, '<p>test</p>', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); - $f = _filter_htmlcorrector('test<hr />'); + $f = Html::normalize('test<hr />'); $this->assertEqual($f, 'test<hr />', 'HTML corrector -- Let proper XHTML pass through.'); - $f = _filter_htmlcorrector('test<hr/>'); + $f = Html::normalize('test<hr/>'); $this->assertEqual($f, 'test<hr />', 'HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.'); - $f = _filter_htmlcorrector('test<hr />'); + $f = Html::normalize('test<hr />'); $this->assertEqual($f, 'test<hr />', 'HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.'); - $f = _filter_htmlcorrector('<span class="test" />'); + $f = Html::normalize('<span class="test" />'); $this->assertEqual($f, '<span class="test"></span>', 'HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.'); - $f = _filter_htmlcorrector('test1<br class="test">test2'); + $f = Html::normalize('test1<br class="test">test2'); $this->assertEqual($f, 'test1<br class="test" />test2', 'HTML corrector -- Automatically close single tags.'); - $f = _filter_htmlcorrector('line1<hr>line2'); + $f = Html::normalize('line1<hr>line2'); $this->assertEqual($f, 'line1<hr />line2', 'HTML corrector -- Automatically close single tags.'); - $f = _filter_htmlcorrector('line1<HR>line2'); + $f = Html::normalize('line1<HR>line2'); $this->assertEqual($f, 'line1<hr />line2', 'HTML corrector -- Automatically close single tags.'); - $f = _filter_htmlcorrector('<img src="http://example.com/test.jpg">test</img>'); + $f = Html::normalize('<img src="http://example.com/test.jpg">test</img>'); $this->assertEqual($f, '<img src="http://example.com/test.jpg" />test', 'HTML corrector -- Automatically close single tags.'); - $f = _filter_htmlcorrector('<br></br>'); + $f = Html::normalize('<br></br>'); $this->assertEqual($f, '<br />', "HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY."); - $f = _filter_htmlcorrector('<div></div>'); + $f = Html::normalize('<div></div>'); $this->assertEqual($f, '<div></div>', "HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY."); - $f = _filter_htmlcorrector('<p>line1<br/><hr/>line2</p>'); + $f = Html::normalize('<p>line1<br/><hr/>line2</p>'); $this->assertEqual($f, '<p>line1<br /></p><hr />line2', 'HTML corrector -- Move non-inline elements outside of inline containers.'); - $f = _filter_htmlcorrector('<p>line1<div>line2</div></p>'); + $f = Html::normalize('<p>line1<div>line2</div></p>'); $this->assertEqual($f, '<p>line1</p><div>line2</div>', 'HTML corrector -- Move non-inline elements outside of inline containers.'); - $f = _filter_htmlcorrector('<p>test<p>test</p>\n'); + $f = Html::normalize('<p>test<p>test</p>\n'); $this->assertEqual($f, '<p>test</p><p>test</p>\n', 'HTML corrector -- Auto-close improperly nested tags.'); - $f = _filter_htmlcorrector('<p>Line1<br><STRONG>bold stuff</b>'); + $f = Html::normalize('<p>Line1<br><STRONG>bold stuff</b>'); $this->assertEqual($f, '<p>Line1<br /><strong>bold stuff</strong></p>', 'HTML corrector -- Properly close unclosed tags, and remove useless closing tags.'); - $f = _filter_htmlcorrector('test <!-- this is a comment -->'); + $f = Html::normalize('test <!-- this is a comment -->'); $this->assertEqual($f, 'test <!-- this is a comment -->', 'HTML corrector -- Do not touch HTML comments.'); - $f = _filter_htmlcorrector('test <!--this is a comment-->'); + $f = Html::normalize('test <!--this is a comment-->'); $this->assertEqual($f, 'test <!--this is a comment-->', 'HTML corrector -- Do not touch HTML comments.'); - $f = _filter_htmlcorrector('test <!-- comment <p>another + $f = Html::normalize('test <!-- comment <p>another <strong>multiple</strong> line comment</p> -->'); $this->assertEqual($f, 'test <!-- comment <p>another <strong>multiple</strong> line comment</p> -->', 'HTML corrector -- Do not touch HTML comments.'); - $f = _filter_htmlcorrector('test <!-- comment <p>another comment</p> -->'); + $f = Html::normalize('test <!-- comment <p>another comment</p> -->'); $this->assertEqual($f, 'test <!-- comment <p>another comment</p> -->', 'HTML corrector -- Do not touch HTML comments.'); - $f = _filter_htmlcorrector('test <!--break-->'); + $f = Html::normalize('test <!--break-->'); $this->assertEqual($f, 'test <!--break-->', 'HTML corrector -- Do not touch HTML comments.'); - $f = _filter_htmlcorrector('<p>test\n</p>\n'); + $f = Html::normalize('<p>test\n</p>\n'); $this->assertEqual($f, '<p>test\n</p>\n', 'HTML corrector -- New-lines are accepted and kept as-is.'); - $f = _filter_htmlcorrector('<p>دروبال'); + $f = Html::normalize('<p>دروبال'); $this->assertEqual($f, '<p>دروبال</p>', 'HTML corrector -- Encoding is correctly kept.'); - $f = _filter_htmlcorrector('<script>alert("test")</script>'); + $f = Html::normalize('<script>alert("test")</script>'); $this->assertEqual($f, '<script> <!--//--><![CDATA[// ><!-- alert("test") //--><!]]> </script>', 'HTML corrector -- CDATA added to script element'); - $f = _filter_htmlcorrector('<p><script>alert("test")</script></p>'); + $f = Html::normalize('<p><script>alert("test")</script></p>'); $this->assertEqual($f, '<p><script> <!--//--><![CDATA[// ><!-- alert("test") //--><!]]> </script></p>', 'HTML corrector -- CDATA added to a nested script element'); - $f = _filter_htmlcorrector('<p><style> /* Styling */ body {color:red}</style></p>'); + $f = Html::normalize('<p><style> /* Styling */ body {color:red}</style></p>'); $this->assertEqual($f, '<p><style> <!--/*--><![CDATA[/* ><!--*/ /* Styling */ body {color:red} /*--><!]]>*/ </style></p>', 'HTML corrector -- CDATA added to a style element.'); - $filtered_data = _filter_htmlcorrector('<p><style> + $filtered_data = Html::normalize('<p><style> /*<![CDATA[*/ /* Styling */ body {color:red} @@ -870,7 +871,7 @@ function testHtmlCorrectorFilter() { format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '/*<![CDATA[*/')) ); - $filtered_data = _filter_htmlcorrector('<p><style> + $filtered_data = Html::normalize('<p><style> <!--/*--><![CDATA[/* ><!--*/ /* Styling */ body {color:red} @@ -889,7 +890,7 @@ function testHtmlCorrectorFilter() { format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '<!--/*--><![CDATA[/* ><!--*/')) ); - $filtered_data = _filter_htmlcorrector('<p><script> + $filtered_data = Html::normalize('<p><script> <!--//--><![CDATA[// ><!-- alert("test"); //--><!]]> @@ -906,7 +907,7 @@ function testHtmlCorrectorFilter() { format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '<!--//--><![CDATA[// ><!--')) ); - $filtered_data = _filter_htmlcorrector('<p><script> + $filtered_data = Html::normalize('<p><script> // <![CDATA[ alert("test"); // ]]> diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php index a42f111bcfeefa182391835b766e28cef87b5e8b..d4655306c3dc312b744b692ca3f90ab46fc39f40 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Common; +use Drupal\Component\Utility\Html; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -616,7 +617,7 @@ function testDrupalRenderChildrenPostRenderCache() { ), ); - $dom = filter_dom_load($cached_element['#markup']); + $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $parent = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Parent"]')->length; $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; @@ -699,7 +700,7 @@ function testDrupalRenderChildrenPostRenderCache() { ), ); - $dom = filter_dom_load($cached_parent_element['#markup']); + $dom = Html::load($cached_parent_element['#markup']); $xpath = new \DOMXPath($dom); $parent = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Parent"]')->length; $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; @@ -724,7 +725,7 @@ function testDrupalRenderChildrenPostRenderCache() { ), ); - $dom = filter_dom_load($cached_child_element['#markup']); + $dom = Html::load($cached_child_element['#markup']); $xpath = new \DOMXPath($dom); $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; $subchild = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div [@class="details-wrapper" and text()="Subchild"]')->length; @@ -806,7 +807,7 @@ function testDrupalRenderRenderCachePlaceholder() { $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = cache()->get(drupal_render_cid_create($element))->data; // Parse unique token out of the markup. - $dom = filter_dom_load($cached_element['#markup']); + $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $token = $nodes->item(0)->getAttribute('token'); diff --git a/core/modules/text/text.module b/core/modules/text/text.module index 675c63e42dad99bedd56814718f79d40c4da6b96..b0eb8fa7edba026a8a56d623429204bf408552c1 100644 --- a/core/modules/text/text.module +++ b/core/modules/text/text.module @@ -5,6 +5,7 @@ * Defines simple text field types. */ +use Drupal\Component\Utility\Html; use Drupal\Core\Entity\EntityInterface; /** @@ -170,7 +171,7 @@ function text_summary($text, $format = NULL, $size = NULL) { // If the htmlcorrector filter is present, apply it to the generated summary. if (isset($format) && $filters->has('filter_htmlcorrector') && $filters->get('filter_htmlcorrector')->status) { - $summary = _filter_htmlcorrector($summary); + $summary = Html::normalize($summary); } return $summary; diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php index aaf6e894d08081a61ec2a407cd9858319dde76bb..5f66de4db4ef7c591baf30f56ad78868d3fde52f 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php @@ -7,6 +7,7 @@ namespace Drupal\views\Plugin\views\field; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\String; use Drupal\views\Plugin\views\HandlerBase; use Drupal\views\Plugin\views\display\DisplayPluginBase; @@ -1681,7 +1682,7 @@ public static function trimText($alter, $value) { } } if (!empty($alter['html'])) { - $value = _filter_htmlcorrector($value); + $value = Html::normalize($value); } return $value;