diff --git a/core/lib/Drupal/Core/Theme/ThemeManager.php b/core/lib/Drupal/Core/Theme/ThemeManager.php index 31c10a7..7add727 100644 --- a/core/lib/Drupal/Core/Theme/ThemeManager.php +++ b/core/lib/Drupal/Core/Theme/ThemeManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Theme; +use Drupal\Core\Render\SafeString; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\StackedRouteMatchInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -317,7 +318,10 @@ public function render($hook, array $variables) { $output = ''; if (isset($info['function'])) { if (function_exists($info['function'])) { - $output = SafeMarkup::set($info['function']($variables)); + // Theme functions do not render via the theme engine, so the output + // is not autoescaped. However, we can only presume that the theme + // function has been written correctly and that the markup is safe. + $output = SafeString::create($info['function']($variables)); } } else { @@ -387,7 +391,7 @@ public function render($hook, array $variables) { $output = $render_function($template_file, $variables); } - return (string) $output; + return ($output instanceof SafeString) ? $output : (string) $output; } /** diff --git a/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php b/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php index ebbfcb5..b61ff84 100644 --- a/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php +++ b/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php @@ -26,8 +26,8 @@ * @param array $variables * An associative array of theme variables. * - * @return string - * The rendered output. + * @return string|\Drupal\Component\Utility\SafeStringInterface + * The rendered output, or a SafeString object. */ public function render($hook, array $variables); diff --git a/core/modules/simpletest/src/AssertContentTrait.php b/core/modules/simpletest/src/AssertContentTrait.php index 1918d42..bc66e27 100644 --- a/core/modules/simpletest/src/AssertContentTrait.php +++ b/core/modules/simpletest/src/AssertContentTrait.php @@ -812,7 +812,7 @@ protected function assertThemeOutput($callback, array $variables = array(), $exp /** @var \Drupal\Core\Render\RendererInterface $renderer */ $renderer = \Drupal::service('renderer'); - $output = $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) { + $output = (string) $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) { return \Drupal::theme()->render($callback, $variables); }); $this->verbose( diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/src/Tests/Theme/ThemeTest.php index c2c0e0b..7d2704f 100644 --- a/core/modules/system/src/Tests/Theme/ThemeTest.php +++ b/core/modules/system/src/Tests/Theme/ThemeTest.php @@ -13,6 +13,7 @@ use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; +use Drupal\Component\Utility\SafeStringInterface; /** * Tests low-level theme functions. @@ -60,11 +61,17 @@ function testAttributeMerging() { */ function testThemeDataTypes() { // theme_test_false is an implemented theme hook so \Drupal::theme() service should - // return a string, even though the theme function itself can return anything. + // return a string or an object that implements SafeStringInterface, even though the + // theme function itself can return anything. $foos = array('null' => NULL, 'false' => FALSE, 'integer' => 1, 'string' => 'foo'); foreach ($foos as $type => $example) { $output = \Drupal::theme()->render('theme_test_foo', array('foo' => $example)); - $this->assertTrue(is_string($output), format_string('\Drupal::theme() returns a string for data type !type.', array('!type' => $type))); + if (is_string($output)) { + $this->assertIdentical($output, '', format_string('\Drupal::theme() returns a string for data type !type.', array('!type' => $type))); + } + else { + $this->assertTrue($output instanceof SafeStringInterface, format_string('\Drupal::theme() returns an object that implements SafeStringInterface for data type !type.', array('!type' => $type))); + } } // suggestionnotimplemented is not an implemented theme hook so \Drupal::theme() service diff --git a/core/modules/system/templates/dropbutton-wrapper.html.twig b/core/modules/system/templates/dropbutton-wrapper.html.twig index ca0ff7e..d4c8d90 100644 --- a/core/modules/system/templates/dropbutton-wrapper.html.twig +++ b/core/modules/system/templates/dropbutton-wrapper.html.twig @@ -12,7 +12,7 @@ * @ingroup themeable */ #} -{% if children %} +{% if children|length %} {% spaceless %}