Commit df701901 authored by catch's avatar catch

Issue #2802923 by Wim Leers, casey: Use <span> instead of <div> for BigPipe placeholders

parent 8d6e9ef5
......@@ -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;
}
......
......@@ -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);
......
......@@ -61,7 +61,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf
);
$status_messages->bigPipePlaceholderId = 'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA';
$status_messages->bigPipePlaceholderRenderArray = [
'#markup' => '<div data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>',
'#markup' => '<span data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;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&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>';
$status_messages->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></span>';
$status_messages->bigPipeNoJsPlaceholderRenderArray = [
'#markup' => '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>',
'#markup' => '<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;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&amp;args[0]&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA"></div>' => $status_messages->placeholderRenderArray,
'<span data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;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&amp;args[0]=llamas&amp;args[1]=suck&amp;token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU';
$exception->bigPipePlaceholderRenderArray = [
'#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></div>',
'#markup' => '<span data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;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&amp;args[0]=llamas&amp;args[1]=suck&amp;token=uhKFNfT4eF449_W-kDQX8E5z4yHyt0-nSHUlwaGAQeU"></div>';
$exception->bigPipeNoJsPlaceholder = '<span data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;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&amp;&amp;token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU';
$embedded_response_exception->bigPipePlaceholderRenderArray = [
'#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></div>',
'#markup' => '<span data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;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&amp;&amp;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&amp;&amp;token=PxOHfS_QL-T01NjBgu7Z7I04tIwMp6La5vM-mVxezbU"></span>';
$embedded_response_exception->bigPipeNoJsPlaceholderRenderArray = [
'#markup' => $embedded_response_exception->bigPipeNoJsPlaceholder,
'#cache' => $cacheability_depends_on_session_and_nojs_cookie,
......
......@@ -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;
......
......@@ -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'
......@@ -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]
];
}
}
......@@ -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');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment