diff --git a/core/modules/big_pipe/src/Render/BigPipe.php b/core/modules/big_pipe/src/Render/BigPipe.php index 976d622e658826c25f25ce12eff39a39f3e29882..17292c281305f9250f41b8b63219d93eca7ea051 100644 --- a/core/modules/big_pipe/src/Render/BigPipe.php +++ b/core/modules/big_pipe/src/Render/BigPipe.php @@ -70,7 +70,7 @@ * 1. BigPipe placeholders: 1 HtmlResponse + N embedded AjaxResponses. * - Before a BigPipe response is sent, it is just a HTML response that * contains BigPipe placeholders. Those placeholders look like - * <div data-big-pipe-placeholder-id="…"></div>. JavaScript is used to + * <span data-big-pipe-placeholder-id="…"></span>. JavaScript is used to * replace those placeholders. * Therefore these placeholders are actually sent to the client. * - The Skeleton of course has attachments, including most notably asset @@ -97,7 +97,7 @@ * - Before a BigPipe response is sent, it is just a HTML response that * contains no-JS BigPipe placeholders. Those placeholders can take two * different forms: - * 1. <div data-big-pipe-nojs-placeholder-id="…"></div> if it's a + * 1. <span data-big-pipe-nojs-placeholder-id="…"></span> if it's a * placeholder that will be replaced by HTML * 2. big_pipe_nojs_placeholder_attribute_safe:… if it's a placeholder * inside a HTML attribute, in which 1. would be invalid (angle brackets @@ -130,11 +130,11 @@ * Combining all of the above, when using both BigPipe placeholders and no-JS * BigPipe placeholders, we therefore send: 1 HtmlResponse + M Embedded HTML * Responses + N Embedded AJAX Responses. Schematically, we send these chunks: - * 1. Byte zero until 1st no-JS placeholder: headers + <html><head /><div>…</div> + * 1. Byte zero until 1st no-JS placeholder: headers + <html><head /><span>…</span> * 2. 1st no-JS placeholder replacement: <link rel="stylesheet" …><script …><content> - * 3. Content until 2nd no-JS placeholder: <div>…</div> + * 3. Content until 2nd no-JS placeholder: <span>…</span> * 4. 2nd no-JS placeholder replacement: <link rel="stylesheet" …><script …><content> - * 5. Content until 3rd no-JS placeholder: <div>…</div> + * 5. Content until 3rd no-JS placeholder: <span>…</span> * 6. [… repeat until all no-JS placeholder replacements are sent …] * 7. Send content after last no-JS placeholder. * 8. Send script_bottom (markup to load bottom i.e. non-critical JS). @@ -714,12 +714,12 @@ protected function renderPlaceholder($placeholder, array $placeholder_render_arr * only keep the first occurrence. */ protected function getPlaceholderOrder($html, $placeholders) { - $fragments = explode('<div data-big-pipe-placeholder-id="', $html); + $fragments = explode('<span data-big-pipe-placeholder-id="', $html); array_shift($fragments); $placeholder_ids = []; foreach ($fragments as $fragment) { - $t = explode('"></div>', $fragment, 2); + $t = explode('"></span>', $fragment, 2); $placeholder_id = $t[0]; $placeholder_ids[] = $placeholder_id; } diff --git a/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php b/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php index 46a20c68da4cebd1075018dddaca42d3d39274a3..f967ebaff96fe9610e8a007135f805a8b9dba33d 100644 --- a/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php +++ b/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php @@ -199,7 +199,7 @@ protected static function createBigPipeJsPlaceholder($original_placeholder, arra $big_pipe_placeholder_id = static::generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array); return [ - '#markup' => '<div data-big-pipe-placeholder-id="' . Html::escape($big_pipe_placeholder_id) . '"></div>', + '#markup' => '<span data-big-pipe-placeholder-id="' . Html::escape($big_pipe_placeholder_id) . '"></span>', '#cache' => [ 'max-age' => 0, 'contexts' => [ @@ -237,7 +237,7 @@ protected static function createBigPipeJsPlaceholder($original_placeholder, arra */ protected static function createBigPipeNoJsPlaceholder($original_placeholder, array $placeholder_render_array, $placeholder_must_be_attribute_safe = FALSE) { if (!$placeholder_must_be_attribute_safe) { - $big_pipe_placeholder = '<div data-big-pipe-nojs-placeholder-id="' . Html::escape(static::generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array)) . '"></div>'; + $big_pipe_placeholder = '<span data-big-pipe-nojs-placeholder-id="' . Html::escape(static::generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array)) . '"></span>'; } else { $big_pipe_placeholder = 'big_pipe_nojs_placeholder_attribute_safe:' . Html::escape($original_placeholder); diff --git a/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php b/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php index 0a674e05df9808c1945443e1707b8ff92cb6a947..69ec86fdc25b103f6245ca6d188a49064709940d 100644 --- a/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php +++ b/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php @@ -61,7 +61,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ); $status_messages->bigPipePlaceholderId = 'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA'; $status_messages->bigPipePlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>', + '#markup' => '<span data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'library' => ['big_pipe/big_pipe'], @@ -75,13 +75,13 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ], ], ]; - $status_messages->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>'; + $status_messages->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></span>'; $status_messages->bigPipeNoJsPlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>', + '#markup' => '<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'big_pipe_nojs_placeholders' => [ - '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>' => $status_messages->placeholderRenderArray, + '<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></span>' => $status_messages->placeholderRenderArray, ], ], ]; @@ -225,7 +225,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ); $current_time->bigPipePlaceholderId = 'timecurrent-timetime'; $current_time->bigPipePlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-placeholder-id="timecurrent-timetime"></div>', + '#markup' => '<span data-big-pipe-placeholder-id="timecurrent-timetime"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'library' => ['big_pipe/big_pipe'], @@ -248,13 +248,13 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf 'settings' => NULL, ], ]; - $current_time->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></div>'; + $current_time->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></span>'; $current_time->bigPipeNoJsPlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></div>', + '#markup' => '<span data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'big_pipe_nojs_placeholders' => [ - '<div data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></div>' => $current_time->placeholderRenderArray, + '<span data-big-pipe-nojs-placeholder-id="timecurrent-timetime"></span>' => $current_time->placeholderRenderArray, ], ], ]; @@ -274,7 +274,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ); $exception->bigPipePlaceholderId = 'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU'; $exception->bigPipePlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></div>', + '#markup' => '<span data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'library' => ['big_pipe/big_pipe'], @@ -289,7 +289,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ], ]; $exception->embeddedAjaxResponseCommands = NULL; - $exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></div>'; + $exception->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></span>'; $exception->bigPipeNoJsPlaceholderRenderArray = [ '#markup' => $exception->bigPipeNoJsPlaceholder, '#cache' => $cacheability_depends_on_session_and_nojs_cookie, @@ -314,7 +314,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ); $embedded_response_exception->bigPipePlaceholderId = 'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU'; $embedded_response_exception->bigPipePlaceholderRenderArray = [ - '#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></div>', + '#markup' => '<span data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></span>', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'library' => ['big_pipe/big_pipe'], @@ -329,7 +329,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf ], ]; $embedded_response_exception->embeddedAjaxResponseCommands = NULL; - $embedded_response_exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></div>'; + $embedded_response_exception->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></span>'; $embedded_response_exception->bigPipeNoJsPlaceholderRenderArray = [ '#markup' => $embedded_response_exception->bigPipeNoJsPlaceholder, '#cache' => $cacheability_depends_on_session_and_nojs_cookie, diff --git a/core/modules/big_pipe/src/Tests/BigPipeTest.php b/core/modules/big_pipe/src/Tests/BigPipeTest.php index 25a59b06cbd4fc8acf318795f9fd27ad811959a7..9d2b25734e3a59e85611d04e254749ab9620ff82 100644 --- a/core/modules/big_pipe/src/Tests/BigPipeTest.php +++ b/core/modules/big_pipe/src/Tests/BigPipeTest.php @@ -357,7 +357,7 @@ protected function assertBigPipePlaceholders(array $expected_big_pipe_placeholde foreach ($expected_big_pipe_placeholders as $big_pipe_placeholder_id => $expected_ajax_response) { $this->pass('BigPipe placeholder: ' . $big_pipe_placeholder_id, 'Debug'); // Verify expected placeholder. - $expected_placeholder_html = '<div data-big-pipe-placeholder-id="' . $big_pipe_placeholder_id . '"></div>'; + $expected_placeholder_html = '<span data-big-pipe-placeholder-id="' . $big_pipe_placeholder_id . '"></span>'; $this->assertRaw($expected_placeholder_html, 'BigPipe placeholder for placeholder ID "' . $big_pipe_placeholder_id . '" found.'); $pos = strpos($this->getRawContent(), $expected_placeholder_html); $placeholder_positions[$pos] = $big_pipe_placeholder_id; 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 index a2fc417a17f73d9b91c9b32e2743bc56f97bc6b6..bd0543b0e7a567a8d6b915bafd358d4d6ae1e812 100644 --- 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 @@ -4,3 +4,10 @@ big_pipe_regression_test.2678662: _controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::regression2678662' requirements: _access: 'TRUE' + +big_pipe_regression_test.2802923: + path: '/big_pipe_regression_test/2802923' + defaults: + _controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::regression2802923' + 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 index f42c89c7102b2bbd8f2f5842bf643abb5be948c2..50e618bee1e125ab1871795367d1ec19c3c78548 100644 --- 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 @@ -17,4 +17,30 @@ public function regression2678662() { ]; } + /** + * @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testMultipleBodies_2678662() + */ + public function regression2802923() { + return [ + '#prefix' => BigPipeMarkup::create('<p>Hi, my train will arrive at '), + 'time' => [ + '#lazy_builder' => [static::class . '::currentTime', []], + '#create_placeholder' => TRUE, + ], + '#suffix' => BigPipeMarkup::create(' — will I still be able to catch the connection to the center?</p>'), + ]; + } + + /** + * #lazy_builder callback; builds <time> markup with current time. + * + * @return array + */ + public static function currentTime() { + return [ + '#markup' => '<time datetime="' . date('Y-m-d', time()) . '"></time>', + '#cache' => ['max-age' => 0] + ]; + } + } diff --git a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php index a99d5fccec801f88c31aaa5aae8cad8f964d8e6e..b4da7b0a9bee14c86f5046f1c4714c8720d54082 100644 --- a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php +++ b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php @@ -184,4 +184,16 @@ public function testMessages_2712935() { } } + /** + * Ensure default BigPipe placeholder HTML cannot split paragraphs. + * + * @see https://www.drupal.org/node/2802923 + */ + public function testPlaceholderInParagraph_2802923() { + $this->drupalLogin($this->drupalCreateUser()); + $this->drupalGet(Url::fromRoute('big_pipe_regression_test.2802923')); + + $this->assertJsCondition('document.querySelectorAll(\'p\').length === 1'); + } + }