diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 3870138..646e428 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -17,6 +17,7 @@ use Drupal\Core\Config\Config; use Drupal\Core\Config\StorageException; use Drupal\Core\Render\AttachmentsInterface; +use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Template\Attribute; use Drupal\Core\Theme\ThemeSettings; @@ -401,17 +402,14 @@ function theme_get_setting($setting_name, $theme = NULL) { * https://www.drupal.org/node/2575065 */ function theme_render_and_autoescape($arg) { - // Bubbles argument's cacheability & attachment metadata if necessary. + // If it's a renderable, then it'll be up to the generated render array it + // returns to contain the necessary cacheability & attachment metadata. If + // it doesn't implement CacheableDependencyInterface or AttachmentsInterface + // then there is nothing to do here. if (!($arg instanceof RenderableInterface) && ($arg instanceof CacheableDependencyInterface || $arg instanceof AttachmentsInterface)) { $arg_bubbleable = []; - if ($arg instanceof CacheableDependencyInterface) { - $arg_bubbleable['#cache']['contexts'] = $arg->getCacheContexts(); - $arg_bubbleable['#cache']['tags'] = $arg->getCacheTags(); - $arg_bubbleable['#cache']['max-age'] = $arg->getCacheMaxAge(); - } - if ($arg instanceof AttachmentsInterface) { - $arg_bubbleable['#attached'] = $arg->getAttachments(); - } + BubbleableMetadata::createFromObject($arg) + ->applyTo($arg_bubbleable); \Drupal::service('renderer')->render($arg_bubbleable); } diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 059d1b5..8754ee1 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -7,6 +7,7 @@ use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Render\AttachmentsInterface; +use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\UrlGeneratorInterface; @@ -491,14 +492,9 @@ protected function bubbleArgMetadata($arg) { } $arg_bubbleable = []; - if ($arg instanceof CacheableDependencyInterface) { - $arg_bubbleable['#cache']['contexts'] = $arg->getCacheContexts(); - $arg_bubbleable['#cache']['tags'] = $arg->getCacheTags(); - $arg_bubbleable['#cache']['max-age'] = $arg->getCacheMaxAge(); - } - if ($arg instanceof AttachmentsInterface) { - $arg_bubbleable['#attached'] = $arg->getAttachments(); - } + BubbleableMetadata::createFromObject($arg) + ->applyTo($arg_bubbleable); + $this->renderer->render($arg_bubbleable); } diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php index cc65ffe..b5e19d3 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php @@ -12,6 +12,7 @@ use Drupal\Core\Link; use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\Markup; +use Drupal\Core\Url; use Drupal\KernelTests\KernelTestBase; /** @@ -93,8 +94,32 @@ public function testThemeEscapeAndRenderNotPrintable() { */ public function testBubblingMetadata() { $link = new GeneratedLink(); - $link->setGeneratedLink(''); + $link->setGeneratedLink(''); $link->addCacheTags(['foo']); + $link->addAttachments(['library' => ['system/base']]); + + $context = new RenderContext(); + // Use a closure here since we need to render with a render context. + $theme_render_and_autoescape = function () use ($link) { + return theme_render_and_autoescape($link); + }; + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $output = $renderer->executeInRenderContext($context, $theme_render_and_autoescape); + $this->assertEquals('', $output); + /** @var \Drupal\Core\Render\BubbleableMetadata $metadata */ + $metadata = $context->pop(); + $this->assertEquals(['foo'], $metadata->getCacheTags()); + $this->assertEquals(['library' => ['system/base']], $metadata->getAttachments()); + } + + /** + * Ensure cache metadata is bubbled when using theme_render_and_autoescape(). + */ + public function testBubblingMetadataWithRenderable() { + $link = new Link('', Url::fromUri('http://example.com')); + $link->addCacheTags(['foo']); + $link->addAttachments(['library' => ['system/base']]); $context = new RenderContext(); // Use a closure here since we need to render with a render context. @@ -105,7 +130,10 @@ public function testBubblingMetadata() { $renderer = \Drupal::service('renderer'); $output = $renderer->executeInRenderContext($context, $theme_render_and_autoescape); $this->assertEquals('', $output); - $this->assertEquals(['foo'], $context->pop()->getCacheTags()); + /** @var \Drupal\Core\Render\BubbleableMetadata $metadata */ + $metadata = $context->pop(); + $this->assertEquals(['foo'], $metadata->getCacheTags()); + $this->assertEquals(['library' => ['system/base']], $metadata->getAttachments()); } } diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index de1ba20..ea93703 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -244,6 +244,7 @@ public function providerTestRenderVar() { /** * @covers ::escapeFilter + * @covers ::bubbleArgMetadata */ public function testEscapeWithGeneratedLink() { $renderer = $this->prophesize(RendererInterface::class); @@ -258,27 +259,44 @@ public function testEscapeWithGeneratedLink() { $twig_extension = new TwigExtension($renderer->reveal()); $twig->addExtension($twig_extension->setUrlGenerator($this->prophesize(UrlGeneratorInterface::class)->reveal())); $link = new GeneratedLink(); - $link->setGeneratedLink(''); + $link->setGeneratedLink(''); $link->addCacheTags(['foo']); + $link->addAttachments(['library' => ['system/base']]); $result = $twig_extension->escapeFilter($twig, $link, 'html', NULL, TRUE); - $renderer->render(["#cache" => ["contexts" => [], "tags" => ["foo"], "max-age" => -1], "#attached" => []])->shouldHaveBeenCalled(); - $this->assertEquals('', $result); + $renderer->render([ + "#cache" => [ + "contexts" => [], + "tags" => ["foo"], + "max-age" => -1 + ], + "#attached" => ['library' => ['system/base']], + ])->shouldHaveBeenCalled(); + $this->assertEquals('', $result); } /** * @covers ::renderVar + * @covers ::bubbleArgMetadata */ public function testRenderVarWithGeneratedLink() { $renderer = $this->prophesize(RendererInterface::class); $twig_extension = new TwigExtension($renderer->reveal()); $link = new GeneratedLink(); - $link->setGeneratedLink(''); + $link->setGeneratedLink(''); $link->addCacheTags(['foo']); + $link->addAttachments(['library' => ['system/base']]); $result = $twig_extension->renderVar($link); - $renderer->render(["#cache" => ["contexts" => [], "tags" => ["foo"], "max-age" => -1], "#attached" => []])->shouldHaveBeenCalled(); - $this->assertEquals('', $result); + $renderer->render([ + "#cache" => [ + "contexts" => [], + "tags" => ["foo"], + "max-age" => -1 + ], + "#attached" => ['library' => ['system/base']], + ])->shouldHaveBeenCalled(); + $this->assertEquals('', $result); } }