Commit 8eb8e89a authored by catch's avatar catch

Issue #2670444 by Wim Leers: Embedded AJAX responses aren't application/json,...

Issue #2670444 by Wim Leers: Embedded AJAX responses aren't application/json, but application/vnd.drupal-ajax
parent 47706f76
......@@ -8,7 +8,7 @@
'use strict';
/**
* Executes Ajax commands in <script type="application/json"> tag.
* Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
*
* These Ajax commands replace placeholders with HTML and load missing CSS/JS.
*
......@@ -52,8 +52,8 @@
* Processes a streamed HTML document receiving placeholder replacements.
*
* @param {HTMLDocument} context
* The HTML document containing <script type="application/json"> tags
* generated by BigPipe.
* The HTML document containing <script type="application/vnd.drupal-ajax">
* tags generated by BigPipe.
*
* @return {bool}
* Returns true when processing has been finished and a stop signal has been
......
......@@ -29,6 +29,20 @@
*/
class BigPipe implements BigPipeInterface {
/**
* The BigPipe placeholder replacements start signal.
*
* @var string
*/
const START_SIGNAL = '<script type="application/vnd.drupal-ajax" data-big-pipe-event="start"></script>';
/**
* The BigPipe placeholder replacements stop signal.
*
* @var string
*/
const STOP_SIGNAL = '<script type="application/vnd.drupal-ajax" data-big-pipe-event="stop"></script>';
/**
* The renderer.
*
......@@ -285,7 +299,8 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
// Send the start signal.
print "\n";
print '<script type="application/json" data-big-pipe-event="start"></script>' . "\n";
print static::START_SIGNAL;
print "\n";
flush();
// A BigPipe response consists of a HTML response plus multiple embedded
......@@ -296,7 +311,7 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
// to be returned.
// @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
$fake_request = $this->requestStack->getMasterRequest()->duplicate();
$fake_request->headers->set('Accept', 'application/json');
$fake_request->headers->set('Accept', 'application/vnd.drupal-ajax');
foreach ($placeholder_order as $placeholder_id) {
if (!isset($placeholders[$placeholder_id])) {
......@@ -332,7 +347,7 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
// Send this embedded AJAX response.
$json = $ajax_response->getContent();
$output = <<<EOF
<script type="application/json" data-big-pipe-replacement-for-placeholder-with-id="$placeholder_id">
<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="$placeholder_id">
$json
</script>
EOF;
......@@ -348,7 +363,9 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
}
// Send the stop signal.
print '<script type="application/json" data-big-pipe-event="stop"></script>' . "\n";
print "\n";
print static::STOP_SIGNAL;
print "\n";
flush();
}
......
......@@ -71,15 +71,15 @@
* sent first, the closing </body> tag is not yet sent, and the connection
* is kept open. Whenever another BigPipe Placeholder is rendered, Drupal
* sends (and so actually appends to the already-sent HTML) something like
* <script type="application/json">[{"command":"settings","settings":{…}}, {"command":…}.
* <script type="application/vnd.drupal-ajax">[{"command":"settings","settings":{…}}, {"command":…}.
* - So, for every BigPipe placeholder, we send such a <script
* type="application/json"> tag. And the contents of that tag is exactly
* like an AJAX response. The BigPipe module has JavaScript that listens for
* these and applies them. Let's call it an Embedded AJAX Response (since it
* is embedded in the HTML response). Now for the interesting bit: each of
* those Embedded AJAX Responses must also take into account the cumulative
* AJAX page state of the HTML document and all preceding Embedded AJAX
* responses.
* type="application/vnd.drupal-ajax"> tag. And the contents of that tag is
* exactly like an AJAX response. The BigPipe module has JavaScript that
* listens for these and applies them. Let's call it an Embedded AJAX
* Response (since it is embedded in the HTML response). Now for the
* interesting bit: each of those Embedded AJAX Responses must also take
* into account the cumulative AJAX page state of the HTML document and all
* preceding Embedded AJAX responses.
* 2. No-JS BigPipe placeholders: 1 HtmlResponse + N embedded HtmlResponses.
* - Before a BigPipe response is sent, it is just a HTML response that
* contains no-JS BigPipe placeholders. Those placeholders can take two
......@@ -125,8 +125,8 @@
* 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).
* 9. 1st placeholder replacement: <script type="application/json">[{"command":"settings","settings":{…}}, {"command":…}
* 10. 2nd placeholder replacement: <script type="application/json">[{"command":"settings","settings":{…}}, {"command":…}
* 9. 1st placeholder replacement: <script type="application/vnd.drupal-ajax">[{"command":"settings","settings":{…}}, {"command":…}
* 10. 2nd placeholder replacement: <script type="application/vnd.drupal-ajax">[{"command":"settings","settings":{…}}, {"command":…}
* 11. [… repeat until all placeholder replacements are sent …]
* 12. Send </body> and everything after it.
* 13. Terminate request/response cycle.
......
......@@ -8,6 +8,7 @@
namespace Drupal\big_pipe\Tests;
use Drupal\big_pipe\Render\Placeholder\BigPipeStrategy;
use Drupal\big_pipe\Render\BigPipe;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
......@@ -26,9 +27,6 @@
*/
class BigPipeTest extends WebTestBase {
const START_SIGNAL= '<script type="application/json" data-big-pipe-event="start"></script>';
const STOP_SIGNAL= '<script type="application/json" data-big-pipe-event="stop"></script>';
/**
* Modules to enable.
*
......@@ -218,8 +216,8 @@ public function testBigPipeNoJs() {
$this->pass('Verifying there are no BigPipe placeholders & replacements…', 'Debug');
$this->assertEqual('<none>', $this->drupalGetHeader('BigPipe-Test-Placeholders'));
$this->pass('Verifying BigPipe start/stop signals are absent…', 'Debug');
$this->assertNoRaw(static::START_SIGNAL, 'BigPipe start signal absent.');
$this->assertNoRaw(static::STOP_SIGNAL, 'BigPipe stop signal absent.');
$this->assertNoRaw(BigPipe::START_SIGNAL, 'BigPipe start signal absent.');
$this->assertNoRaw(BigPipe::STOP_SIGNAL, 'BigPipe stop signal absent.');
$this->pass('Verifying BigPipe assets are absent…', 'Debug');
$this->assertFalse(empty($this->getDrupalSettings()), 'drupalSettings and BigPipe asset library absent.');
......@@ -273,7 +271,7 @@ protected function assertBigPipePlaceholders(array $expected_big_pipe_placeholde
// Verify expected placeholder replacement.
$result = $this->xpath('//script[@data-big-pipe-replacement-for-placeholder-with-id=:id]', [':id' => Html::decodeEntities($big_pipe_placeholder_id)]);
$this->assertEqual($expected_ajax_response, trim((string) $result[0]));
$expected_placeholder_replacement = '<script type="application/json" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
$expected_placeholder_replacement = '<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
$this->assertRaw($expected_placeholder_replacement);
$pos = strpos($this->getRawContent(), $expected_placeholder_replacement);
$placeholder_replacement_positions[$pos] = $big_pipe_placeholder_id;
......@@ -282,22 +280,22 @@ protected function assertBigPipePlaceholders(array $expected_big_pipe_placeholde
$this->assertEqual(array_keys($expected_big_pipe_placeholders), array_values($placeholder_positions));
$this->assertEqual(count($expected_big_pipe_placeholders), preg_match_all('/' . preg_quote('<div data-big-pipe-placeholder-id="', '/') . '/', $this->getRawContent()));
$this->assertEqual(array_keys($expected_big_pipe_placeholders), array_values($placeholder_replacement_positions));
$this->assertEqual(count($expected_big_pipe_placeholders), preg_match_all('/' . preg_quote('<script type="application/json" data-big-pipe-replacement-for-placeholder-with-id="', '/') . '/', $this->getRawContent()));
$this->assertEqual(count($expected_big_pipe_placeholders), preg_match_all('/' . preg_quote('<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="', '/') . '/', $this->getRawContent()));
$this->pass('Verifying BigPipe start/stop signals…', 'Debug');
$this->assertRaw(static::START_SIGNAL, 'BigPipe start signal present.');
$this->assertRaw(static::STOP_SIGNAL, 'BigPipe stop signal present.');
$start_signal_position = strpos($this->getRawContent(), static::START_SIGNAL);
$stop_signal_position = strpos($this->getRawContent(), static::STOP_SIGNAL);
$this->assertRaw(BigPipe::START_SIGNAL, 'BigPipe start signal present.');
$this->assertRaw(BigPipe::STOP_SIGNAL, 'BigPipe stop signal present.');
$start_signal_position = strpos($this->getRawContent(), BigPipe::START_SIGNAL);
$stop_signal_position = strpos($this->getRawContent(), BigPipe::STOP_SIGNAL);
$this->assertTrue($start_signal_position < $stop_signal_position, 'BigPipe start signal appears before stop signal.');
$this->pass('Verifying BigPipe placeholder replacements and start/stop signals were streamed in the correct order…', 'Debug');
$expected_stream_order = array_keys($expected_big_pipe_placeholders);
array_unshift($expected_stream_order, static::START_SIGNAL);
array_push($expected_stream_order, static::STOP_SIGNAL);
array_unshift($expected_stream_order, BigPipe::START_SIGNAL);
array_push($expected_stream_order, BigPipe::STOP_SIGNAL);
$actual_stream_order = $placeholder_replacement_positions + [
$start_signal_position => static::START_SIGNAL,
$stop_signal_position => static::STOP_SIGNAL,
$start_signal_position => BigPipe::START_SIGNAL,
$stop_signal_position => BigPipe::STOP_SIGNAL,
];
ksort($actual_stream_order, SORT_NUMERIC);
$this->assertEqual($expected_stream_order, array_values($actual_stream_order));
......
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