From 0cbd31dbb8e44ec8307a416e6e6fe9affe64cf9e Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Thu, 23 Sep 2021 23:59:06 +0100
Subject: [PATCH] Issue #3230772 by larowlan, phenaproxima:
 OembedMediaController doesn't properly bubble cacheability
 metadata/attachments

---
 .../src/Controller/OEmbedIframeController.php    | 16 +++++++++++++++-
 .../tests/modules/media_test_oembed/css/test.css |  5 +++++
 .../media_test_oembed.libraries.yml              |  5 +++++
 .../media_test_oembed/media_test_oembed.module   |  3 +++
 .../src/Kernel/OEmbedIframeControllerTest.php    | 11 ++++++++---
 5 files changed, 36 insertions(+), 4 deletions(-)
 create mode 100644 core/modules/media/tests/modules/media_test_oembed/css/test.css
 create mode 100644 core/modules/media/tests/modules/media_test_oembed/media_test_oembed.libraries.yml

diff --git a/core/modules/media/src/Controller/OEmbedIframeController.php b/core/modules/media/src/Controller/OEmbedIframeController.php
index 2a6c0eb8fa51..dc983391651b 100644
--- a/core/modules/media/src/Controller/OEmbedIframeController.php
+++ b/core/modules/media/src/Controller/OEmbedIframeController.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\Core\Render\HtmlResponse;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Render\RendererInterface;
@@ -167,7 +168,8 @@ public function render(Request $request) {
         ],
         '#placeholder_token' => $placeholder_token,
       ];
-      $content = $this->renderer->executeInRenderContext(new RenderContext(), function () use ($resource, $element) {
+      $context = new RenderContext();
+      $content = $this->renderer->executeInRenderContext($context, function () use ($resource, $element) {
         return $this->renderer->render($element);
       });
       $response
@@ -175,6 +177,18 @@ public function render(Request $request) {
         ->setAttachments($element['#attached'])
         ->addCacheableDependency($resource)
         ->addCacheableDependency(CacheableMetadata::createFromRenderArray($element));
+
+      // Modules and themes implementing hook_media_oembed_iframe_preprocess()
+      // can add additional #cache and #attachments to a render array. If this
+      // occurs, the render context won't be empty, and we need to ensure the
+      // added metadata is bubbled up to the response.
+      // @see \Drupal\Core\Theme\ThemeManager::render()
+      if (!$context->isEmpty()) {
+        $bubbleable_metadata = $context->pop();
+        assert($bubbleable_metadata instanceof BubbleableMetadata);
+        $response->addCacheableDependency($bubbleable_metadata);
+        $response->addAttachments($bubbleable_metadata->getAttachments());
+      }
     }
     catch (ResourceException $e) {
       // Prevent the response from being cached.
diff --git a/core/modules/media/tests/modules/media_test_oembed/css/test.css b/core/modules/media/tests/modules/media_test_oembed/css/test.css
new file mode 100644
index 000000000000..4903225ba055
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_oembed/css/test.css
@@ -0,0 +1,5 @@
+/**
+ * This is an empty file by design.
+ * @see \Drupal\Tests\media\Kernel\OEmbedIframeControllerTest::testResourcePassedToPreprocess()
+ * @see media_test_oembed_preprocess_media_oembed_iframe()
+ */
diff --git a/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.libraries.yml b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.libraries.yml
new file mode 100644
index 000000000000..65d7c52f8c8a
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.libraries.yml
@@ -0,0 +1,5 @@
+frame:
+  version: VERSION
+  css:
+    component:
+      css/test.css: { preprocess: false, minified: true }
diff --git a/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
index 2e19d9faceb6..7ab7f1249689 100644
--- a/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
+++ b/core/modules/media/tests/modules/media_test_oembed/media_test_oembed.module
@@ -14,6 +14,9 @@ function media_test_oembed_preprocess_media_oembed_iframe(array &$variables) {
   if ($variables['resource']->getProvider()->getName() === 'YouTube') {
     $variables['media'] = str_replace('?feature=oembed', '?feature=oembed&pasta=rigatoni', (string) $variables['media']);
   }
+  // @see \Drupal\Tests\media\Kernel\OEmbedIframeControllerTest
+  $variables['#attached']['library'][] = 'media_test_oembed/frame';
+  $variables['#cache']['tags'][] = 'yo_there';
 }
 
 /**
diff --git a/core/modules/media/tests/src/Kernel/OEmbedIframeControllerTest.php b/core/modules/media/tests/src/Kernel/OEmbedIframeControllerTest.php
index 3b702a6ef74a..0bc8c897e985 100644
--- a/core/modules/media/tests/src/Kernel/OEmbedIframeControllerTest.php
+++ b/core/modules/media/tests/src/Kernel/OEmbedIframeControllerTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\media\Kernel;
 
+use Drupal\Core\Render\HtmlResponse;
 use Drupal\media\Controller\OEmbedIframeController;
 use Drupal\media\OEmbed\Provider;
 use Drupal\media\OEmbed\Resource;
@@ -93,13 +94,17 @@ public function testResourcePassedToPreprocess() {
       'url' => '',
       'hash' => $hash,
     ]);
-    $content = OEmbedIframeController::create($this->container)
-      ->render($request)
-      ->getContent();
+    $response = $this->container->get('html_response.attachments_processor')
+      ->processAttachments(OEmbedIframeController::create($this->container)
+        ->render($request));
+    assert($response instanceof HtmlResponse);
+    $content = $response->getContent();
 
     // This query parameter is added by
     // media_test_oembed_preprocess_media_oembed_iframe() for YouTube videos.
     $this->assertStringContainsString('&pasta=rigatoni', $content);
+    $this->assertStringContainsString('test.css', $content);
+    $this->assertContains('yo_there', $response->getCacheableMetadata()->getCacheTags());
   }
 
 }
-- 
GitLab