Commit 5ef7c978 authored by catch's avatar catch

Issue #2931443 by Wim Leers, casey, zuuperman, drpal: Browser parsing race...

Issue #2931443 by Wim Leers, casey, zuuperman, drpal: Browser parsing race condition: JSON.parse sometimes called on partially streamed BigPipe placholder replacements, causes fatal JS error
parent 259bf401
......@@ -20,16 +20,17 @@
// Ignore any placeholders that are not in the known placeholder list. Used
// to avoid someone trying to XSS the site via the placeholdering mechanism.
if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
const response = mapTextContentToAjaxResponse(content);
// If we try to parse the content too early (when the JSON containing Ajax
// commands is still arriving), textContent will be empty which will cause
// JSON.parse() to fail. Remove once so that it can be processed again
// later.
// @see bigPipeProcessDocument()
if (content === '') {
// commands is still arriving), textContent will be empty or incomplete.
if (response === false) {
/**
* Mark as unprocessed so this will be retried later.
* @see bigPipeProcessDocument()
*/
$(this).removeOnce('big-pipe');
}
else {
const response = JSON.parse(content);
// Create a Drupal.Ajax object without associating an element, a
// progress indicator or a URL.
const ajaxObject = Drupal.ajax({
......@@ -45,6 +46,28 @@
}
}
/**
* Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
*
* @param {string} content
* The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
* @return {Array|boolean}
* The parsed Ajax response containing an array of Ajax commands, or false in
* case the DOM node hasn't fully arrived yet.
*/
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
}
catch (e) {
return false;
}
}
/**
* Processes a streamed HTML document receiving placeholder replacements.
*
......
......@@ -11,11 +11,11 @@
var content = this.textContent.trim();
if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
if (content === '') {
var response = mapTextContentToAjaxResponse(content);
if (response === false) {
$(this).removeOnce('big-pipe');
} else {
var response = JSON.parse(content);
var ajaxObject = Drupal.ajax({
url: '',
base: false,
......@@ -28,6 +28,18 @@
}
}
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
} catch (e) {
return false;
}
}
function bigPipeProcessDocument(context) {
if (!context.querySelector('script[data-big-pipe-event="start"]')) {
return false;
......
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