diff --git a/core/modules/big_pipe/src/Render/BigPipe.php b/core/modules/big_pipe/src/Render/BigPipe.php index 5e3cc120320e3cdbbf95f2a97fbddaed710d305e..91452085b00475f4453019da3fd6ed1caeab931e 100644 --- a/core/modules/big_pipe/src/Render/BigPipe.php +++ b/core/modules/big_pipe/src/Render/BigPipe.php @@ -126,7 +126,13 @@ public function sendContent($content, array $attachments) { // Reopen it for the duration that we are rendering placeholders. $this->session->start(); - list($pre_body, $post_body) = explode('</body>', $content, 2); + // Find the closing </body> tag and get the strings before and after. But be + // careful to use the latest occurrence of the string "</body>", to ensure + // that strings in inline JavaScript or CDATA sections aren't used instead. + $parts = explode('</body>', $content); + $post_body = array_pop($parts); + $pre_body = implode('', $parts); + $this->sendPreBody($pre_body, $nojs_placeholders, $cumulative_assets); $this->sendPlaceholders($placeholders, $this->getPlaceholderOrder($pre_body), $cumulative_assets); $this->sendPostBody($post_body); diff --git a/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.info.yml b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..d8b9a35363b35bdfe7590e305bc80b370bd022d7 --- /dev/null +++ b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.info.yml @@ -0,0 +1,6 @@ +name: 'BigPipe regression test' +type: module +description: 'Support module for BigPipe regression testing.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.routing.yml b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..a2fc417a17f73d9b91c9b32e2743bc56f97bc6b6 --- /dev/null +++ b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/big_pipe_regression_test.routing.yml @@ -0,0 +1,6 @@ +big_pipe_regression_test.2678662: + path: '/big_pipe_regression_test/2678662' + defaults: + _controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::regression2678662' + requirements: + _access: 'TRUE' diff --git a/core/modules/big_pipe/tests/modules/big_pipe_regression_test/src/BigPipeRegressionTestController.php b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/src/BigPipeRegressionTestController.php new file mode 100644 index 0000000000000000000000000000000000000000..f42c89c7102b2bbd8f2f5842bf643abb5be948c2 --- /dev/null +++ b/core/modules/big_pipe/tests/modules/big_pipe_regression_test/src/BigPipeRegressionTestController.php @@ -0,0 +1,20 @@ +<?php + +namespace Drupal\big_pipe_regression_test; + +use Drupal\big_pipe\Render\BigPipeMarkup; + +class BigPipeRegressionTestController { + + const MARKER_2678662 = '<script>var hitsTheFloor = "</body>";</script>'; + + /** + * @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testMultipleBodies_2678662() + */ + public function regression2678662() { + return [ + '#markup' => BigPipeMarkup::create(self::MARKER_2678662), + ]; + } + +} diff --git a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php index ab28073586c9d09afbbf84df6422d00c369c3ccc..3ce990797f180d6a49f065c6fd46134aa5709897 100644 --- a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php +++ b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php @@ -2,10 +2,13 @@ namespace Drupal\Tests\big_pipe\FunctionalJavascript; +use Drupal\big_pipe\Render\BigPipe; +use Drupal\big_pipe_regression_test\BigPipeRegressionTestController; use Drupal\comment\CommentInterface; use Drupal\comment\Entity\Comment; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\comment\Tests\CommentTestTrait; +use Drupal\Core\Url; use Drupal\editor\Entity\Editor; use Drupal\filter\Entity\FilterFormat; use Drupal\FunctionalJavascriptTests\JavascriptTestBase; @@ -30,6 +33,7 @@ class BigPipeRegressionTest extends JavascriptTestBase { 'node', 'comment', 'big_pipe', + 'big_pipe_regression_test', 'history', 'editor', 'ckeditor', @@ -114,4 +118,29 @@ public function testCommentForm_2698811() { $this->assertJsCondition($javascript); } + /** + * Ensure BigPipe works despite inline JS containing the string "</body>". + * + * @see https://www.drupal.org/node/2678662 + */ + public function testMultipleClosingBodies_2678662() { + $this->drupalLogin($this->drupalCreateUser()); + $this->drupalGet(Url::fromRoute('big_pipe_regression_test.2678662')); + + // Confirm that AJAX behaviors were instantiated, if not, this points to a + // JavaScript syntax error. + $javascript = <<<JS + (function(){ + return Object.keys(Drupal.ajax.instances).length > 0; + }()); +JS; + $this->assertJsCondition($javascript); + + // Besides verifying there is no JavaScript syntax error, also verify the + // HTML structure. + $this->assertSession()->responseContains(BigPipe::STOP_SIGNAL . "\n\n\n</body></html>", 'The BigPipe stop signal is present just before the closing </body> and </html> tags.'); + $js_code_until_closing_body_tag = substr(BigPipeRegressionTestController::MARKER_2678662, 0, strpos(BigPipeRegressionTestController::MARKER_2678662, '</body>')); + $this->assertSession()->responseNotContains($js_code_until_closing_body_tag . "\n" . BigPipe::START_SIGNAL, 'The BigPipe start signal does NOT start at the closing </body> tag string in an inline script.'); + } + }