3 * Renders BigPipe placeholders using Drupal's Ajax system.
6 (function ($, Drupal, drupalSettings) {
8 * Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
10 * These Ajax commands replace placeholders with HTML and load missing CSS/JS.
12 * @param {number} index
14 * @param {HTMLScriptElement} placeholderReplacement
15 * Script tag created by BigPipe.
17 function bigPipeProcessPlaceholderReplacement(index, placeholderReplacement) {
18 const placeholderId = placeholderReplacement.getAttribute('data-big-pipe-replacement-for-placeholder-with-id');
19 const content = this.textContent.trim();
20 // Ignore any placeholders that are not in the known placeholder list. Used
21 // to avoid someone trying to XSS the site via the placeholdering mechanism.
22 if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
23 const response = mapTextContentToAjaxResponse(content);
24 // If we try to parse the content too early (when the JSON containing Ajax
25 // commands is still arriving), textContent will be empty or incomplete.
26 if (response === false) {
28 * Mark as unprocessed so this will be retried later.
29 * @see bigPipeProcessDocument()
31 $(this).removeOnce('big-pipe');
34 // Create a Drupal.Ajax object without associating an element, a
35 // progress indicator or a URL.
36 const ajaxObject = Drupal.ajax({
42 // Then, simulate an AJAX response having arrived, and let the Ajax
44 ajaxObject.success(response, 'success');
50 * Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
52 * @param {string} content
53 * The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
54 * @return {Array|boolean}
55 * The parsed Ajax response containing an array of Ajax commands, or false in
56 * case the DOM node hasn't fully arrived yet.
58 function mapTextContentToAjaxResponse(content) {
64 return JSON.parse(content);
72 * Processes a streamed HTML document receiving placeholder replacements.
74 * @param {HTMLDocument} context
75 * The HTML document containing <script type="application/vnd.drupal-ajax">
76 * tags generated by BigPipe.
79 * Returns true when processing has been finished and a stop signal has been
82 function bigPipeProcessDocument(context) {
83 // Make sure we have BigPipe-related scripts before processing further.
84 if (!context.querySelector('script[data-big-pipe-event="start"]')) {
88 $(context).find('script[data-big-pipe-replacement-for-placeholder-with-id]')
90 .each(bigPipeProcessPlaceholderReplacement);
92 // If we see the stop signal, clear the timeout: all placeholder
93 // replacements are guaranteed to be received and processed.
94 if (context.querySelector('script[data-big-pipe-event="stop"]')) {
96 clearTimeout(timeoutID);
104 function bigPipeProcess() {
105 timeoutID = setTimeout(() => {
106 if (!bigPipeProcessDocument(document)) {
112 // The frequency with which to check for newly arrived BigPipe placeholders.
113 // Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
114 // more would cause the user to see content appear noticeably slower.
115 const interval = drupalSettings.bigPipeInterval || 50;
116 // The internal ID to contain the watcher service.
121 // If something goes wrong, make sure everything is cleaned up and has had a
122 // chance to be processed with everything loaded.
123 $(window).on('load', () => {
125 clearTimeout(timeoutID);
127 bigPipeProcessDocument(document);
129 }(jQuery, Drupal, drupalSettings));