From e770c3846f74e5c3d43b3d258e712f5feb3dbd0f Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 25 Jan 2016 13:45:23 +0000
Subject: [PATCH] Issue #2567561 by Sagar Ramgade, mbaynton, Cottser, gnuget,
 Wim Leers: Captioned elements and their children are removed when theme
 debugging on

---
 .../src/Plugin/Filter/FilterCaption.php       |  18 +--
 .../src/Tests/FilterCaptionTwigDebugTest.php  | 109 ++++++++++++++++++
 2 files changed, 119 insertions(+), 8 deletions(-)
 create mode 100644 core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php

diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index 76a11258303e..627379e8ace3 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -72,16 +72,18 @@ public function process($text, $langcode) {
         $altered_html = drupal_render($filter_caption);
 
         // Load the altered HTML into a new DOMDocument and retrieve the element.
-        $updated_node = Html::load($altered_html)->getElementsByTagName('body')
+        $updated_nodes = Html::load($altered_html)->getElementsByTagName('body')
           ->item(0)
-          ->childNodes
-          ->item(0);
+          ->childNodes;
 
-        // Import the updated node from the new DOMDocument into the original
-        // one, importing also the child nodes of the updated node.
-        $updated_node = $dom->importNode($updated_node, TRUE);
-        // Finally, replace the original node with the new node.
-        $node->parentNode->replaceChild($updated_node, $node);
+        foreach ($updated_nodes as $updated_node) {
+          // Import the updated node from the new DOMDocument into the original
+          // one, importing also the child nodes of the updated node.
+          $updated_node = $dom->importNode($updated_node, TRUE);
+          $node->parentNode->insertBefore($updated_node, $node);
+        }
+        // Finally, remove the original data-caption node.
+        $node->parentNode->removeChild($node);
       }
 
       $result->setProcessedText(Html::serialize($dom))
diff --git a/core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php b/core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php
new file mode 100644
index 000000000000..8b3d7cabbd2b
--- /dev/null
+++ b/core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Tests\FilterCaptionTwigDebugTest.
+ */
+
+namespace Drupal\filter\Tests;
+
+use Drupal\Core\Render\RenderContext;
+use Drupal\simpletest\WebTestBase;
+use Drupal\filter\FilterPluginCollection;
+
+/**
+ * Tests the caption filter with Twig debugging on.
+ *
+ * @group filter
+ */
+class FilterCaptionTwigDebugTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['system', 'filter'];
+
+  /**
+   * @var \Drupal\filter\Plugin\FilterInterface[]
+   */
+  protected $filters;
+
+  /**
+   * Enables Twig debugging.
+   */
+  protected function debugOn() {
+    // Enable debug, rebuild the service container, and clear all caches.
+    $parameters = $this->container->getParameter('twig.config');
+    if (!$parameters['debug']) {
+      $parameters['debug'] = TRUE;
+      $this->setContainerParameter('twig.config', $parameters);
+      $this->rebuildContainer();
+      $this->resetAll();
+    }
+  }
+
+  /**
+   * Disables Twig debugging.
+   */
+  protected function debugOff() {
+    // Disable debug, rebuild the service container, and clear all caches.
+    $parameters = $this->container->getParameter('twig.config');
+    if ($parameters['debug']) {
+      $parameters['debug'] = FALSE;
+      $this->setContainerParameter('twig.config', $parameters);
+      $this->rebuildContainer();
+      $this->resetAll();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->debugOn();
+
+    $manager = $this->container->get('plugin.manager.filter');
+    $bag = new FilterPluginCollection($manager, []);
+    $this->filters = $bag->getAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function tearDown() {
+    $this->debugOff();
+  }
+
+  /**
+   * Test the caption filter with Twig debugging on.
+   */
+  function testCaptionFilter() {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+    $filter = $this->filters['filter_caption'];
+
+    $test = function ($input) use ($filter, $renderer) {
+      return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter) {
+        return $filter->process($input, 'und');
+      });
+    };
+
+    // No data-caption attribute.
+    $input = '<img src="llama.jpg" />';
+    $expected = $input;
+    $this->assertIdentical($expected, $test($input)->getProcessedText());
+
+    // Data-caption attribute.
+    $input = '<img src="llama.jpg" data-caption="Loquacious llama!" />';
+    $expected = '<img src="llama.jpg" /><figcaption>Loquacious llama!</figcaption>';
+    $output = $test($input);
+    $output = $output->getProcessedText();
+    $this->assertTrue(strpos($output, $expected) !== FALSE, "\"$output\" contains \"$expected\"");
+    $this->assertTrue(strpos($output, '<!-- THEME HOOK: \'filter_caption\' -->') !== FALSE, 'filter_caption theme hook debug comment is present.');
+  }
+
+}
-- 
GitLab