3 namespace Drupal\Core\Render\Element;
5 use Drupal\Component\Render\MarkupInterface;
6 use Drupal\Component\Utility\Html as HtmlUtility;
7 use Drupal\Core\Render\Markup;
8 use Drupal\Component\Utility\Xss;
9 use Drupal\Core\Template\Attribute;
12 * Provides a render element for any HTML tag, with properties and value.
15 * - #tag: The tag name to output.
16 * - #attributes: (array, optional) HTML attributes to apply to the tag. The
17 * attributes are escaped, see \Drupal\Core\Template\Attribute.
18 * - #value: (string, optional) A string containing the textual contents of
20 * - #noscript: (bool, optional) When set to TRUE, the markup
21 * (including any prefix or suffix) will be wrapped in a <noscript> element.
26 * '#type' => 'html_tag',
28 * '#value' => $this->t('Hello World'),
32 * @RenderElement("html_tag")
34 class HtmlTag extends RenderElement {
37 * Void elements do not contain values or closing tags.
38 * @see http://www.w3.org/TR/html5/syntax.html#syntax-start-tag
39 * @see http://www.w3.org/TR/html5/syntax.html#void-elements
41 static protected $voidElements = [
42 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
43 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr',
49 public function getInfo() {
50 $class = get_class($this);
53 [$class, 'preRenderConditionalComments'],
54 [$class, 'preRenderHtmlTag'],
62 * Pre-render callback: Renders a generic HTML tag with attributes into #markup.
64 * @param array $element
65 * An associative array containing:
66 * - #tag: The tag name to output. Typical tags added to the HTML HEAD:
67 * - meta: To provide meta information, such as a page refresh.
68 * - link: To refer to stylesheets and other contextual information.
69 * - script: To load JavaScript.
70 * The value of #tag is escaped.
71 * - #attributes: (optional) An array of HTML attributes to apply to the
72 * tag. The attributes are escaped, see \Drupal\Core\Template\Attribute.
73 * - #value: (optional) A string containing tag content, such as inline
74 * CSS. The value of #value will be XSS admin filtered if it is not safe.
75 * - #noscript: (optional) If TRUE, the markup (including any prefix or
76 * suffix) will be wrapped in a <noscript> element. (Note that passing
77 * any non-empty value here will add the <noscript> tag.)
81 public static function preRenderHtmlTag($element) {
82 $attributes = isset($element['#attributes']) ? new Attribute($element['#attributes']) : '';
84 // An HTML tag should not contain any special characters. Escape them to
85 // ensure this cannot be abused.
86 $escaped_tag = HtmlUtility::escape($element['#tag']);
87 $markup = '<' . $escaped_tag . $attributes;
88 // Construct a void element.
89 if (in_array($element['#tag'], self::$voidElements)) {
92 // Construct all other elements.
95 $markup .= $element['#value'] instanceof MarkupInterface ? $element['#value'] : Xss::filterAdmin($element['#value']);
96 $markup .= '</' . $escaped_tag . ">\n";
98 if (!empty($element['#noscript'])) {
99 $markup = "<noscript>$markup</noscript>";
101 $element['#markup'] = Markup::create($markup);
106 * Pre-render callback: Renders #browsers into #prefix and #suffix.
108 * @param array $element
109 * A render array with a '#browsers' property. The '#browsers' property can
110 * contain any or all of the following keys:
111 * - 'IE': If FALSE, the element is not rendered by Internet Explorer. If
112 * TRUE, the element is rendered by Internet Explorer. Can also be a string
113 * containing an expression for Internet Explorer to evaluate as part of a
114 * conditional comment. For example, this can be set to 'lt IE 7' for the
115 * element to be rendered in Internet Explorer 6, but not in Internet
116 * Explorer 7 or higher. Defaults to TRUE.
117 * - '!IE': If FALSE, the element is not rendered by browsers other than
118 * Internet Explorer. If TRUE, the element is rendered by those browsers.
121 * - To render an element in all browsers, '#browsers' can be left out or set
122 * to array('IE' => TRUE, '!IE' => TRUE).
123 * - To render an element in Internet Explorer only, '#browsers' can be set
124 * to array('!IE' => FALSE).
125 * - To render an element in Internet Explorer 6 only, '#browsers' can be set
126 * to array('IE' => 'lt IE 7', '!IE' => FALSE).
127 * - To render an element in Internet Explorer 8 and higher and in all other
128 * browsers, '#browsers' can be set to array('IE' => 'gte IE 8').
131 * The passed-in element with markup for conditional comments potentially
132 * added to '#prefix' and '#suffix'.
134 public static function preRenderConditionalComments($element) {
135 $browsers = isset($element['#browsers']) ? $element['#browsers'] : [];
141 // If rendering in all browsers, no need for conditional comments.
142 if ($browsers['IE'] === TRUE && $browsers['!IE']) {
146 // Determine the conditional comment expression for Internet Explorer to
148 if ($browsers['IE'] === TRUE) {
151 elseif ($browsers['IE'] === FALSE) {
155 // The IE expression might contain some user input data.
156 $expression = Xss::filterAdmin($browsers['IE']);
159 // If the #prefix and #suffix properties are used, wrap them with
160 // conditional comment markup. The conditional comment expression is
161 // evaluated by Internet Explorer only. To control the rendering by other
162 // browsers, use either the "downlevel-hidden" or "downlevel-revealed"
163 // technique. See http://wikipedia.org/wiki/Conditional_comment
166 // Ensure what we are dealing with is safe.
167 // This would be done later anyway in drupal_render().
168 $prefix = isset($element['#prefix']) ? $element['#prefix'] : '';
169 if ($prefix && !($prefix instanceof MarkupInterface)) {
170 $prefix = Xss::filterAdmin($prefix);
172 $suffix = isset($element['#suffix']) ? $element['#suffix'] : '';
173 if ($suffix && !($suffix instanceof MarkupInterface)) {
174 $suffix = Xss::filterAdmin($suffix);
177 // We ensured above that $expression is either a string we created or is
178 // admin XSS filtered, and that $prefix and $suffix are also admin XSS
179 // filtered if they are unsafe. Thus, all these strings are safe.
180 if (!$browsers['!IE']) {
181 // "downlevel-hidden".
182 $element['#prefix'] = Markup::create("\n<!--[if $expression]>\n" . $prefix);
183 $element['#suffix'] = Markup::create($suffix . "<![endif]-->\n");
186 // "downlevel-revealed".
187 $element['#prefix'] = Markup::create("\n<!--[if $expression]><!-->\n" . $prefix);
188 $element['#suffix'] = Markup::create($suffix . "<!--<![endif]-->\n");