Commit 09bcf41f authored by catch's avatar catch
Browse files

Issue #3387039 by dinazaur, smustgrave, fjgarlin, nod_: Large placeholders are not processed

(cherry picked from commit 6f97533d)
parent a6ba4811
Loading
Loading
Loading
Loading
Loading
+45 −14
Original line number Diff line number Diff line
@@ -59,8 +59,9 @@
   */
  function processReplacement(replacement) {
    const id = replacement.dataset.bigPipeReplacementForPlaceholderWithId;
    // Because we use a mutation observer the content is guaranteed to be
    // complete at this point.
    // The content is not guaranteed to be complete at this point, but trimming
    // it will not make a big change, since json will not be valid if it was
    // not fully loaded anyway.
    const content = replacement.textContent.trim();

    // Ignore any placeholders that are not in the known placeholder list. Used
@@ -69,20 +70,32 @@
      return;
    }

    // Immediately remove the replacement to prevent it being processed twice.
    delete drupalSettings.bigPipePlaceholderIds[id];

    const response = mapTextContentToAjaxResponse(content);

    if (response === false) {
      return;
    }

    // Immediately remove the replacement to prevent it being processed twice.
    delete drupalSettings.bigPipePlaceholderIds[id];

    // Then, simulate an AJAX response having arrived, and let the Ajax system
    // handle it.
    ajaxObject.success(response, 'success');
  }

  /**
   * Checks if node is valid big pipe replacement.
   */
  function checkMutation(node) {
    return Boolean(
      node.nodeType === Node.ELEMENT_NODE &&
        node.nodeName === 'SCRIPT' &&
        node.dataset &&
        node.dataset.bigPipeReplacementForPlaceholderWithId,
    );
  }

  /**
   * Check that the element is valid to process and process it.
   *
@@ -90,12 +103,7 @@
   *  The node added to the body element.
   */
  function checkMutationAndProcess(node) {
    if (
      node.nodeType === Node.ELEMENT_NODE &&
      node.nodeName === 'SCRIPT' &&
      node.dataset &&
      node.dataset.bigPipeReplacementForPlaceholderWithId
    ) {
    if (checkMutation(node)) {
      processReplacement(node);
    }
  }
@@ -107,8 +115,20 @@
   *  The list of mutations registered by the browser.
   */
  function processMutations(mutations) {
    mutations.forEach(({ addedNodes }) => {
    mutations.forEach(({ addedNodes, type, target }) => {
      addedNodes.forEach(checkMutationAndProcess);

      // Checks if parent node of target node has not been processed.
      // @see `@ingroup large_chunk` for more information.
      if (
        type === 'characterData' &&
        checkMutation(target.parentNode) &&
        drupalSettings.bigPipePlaceholderIds[
          target.parentNode.dataset.bigPipeReplacementForPlaceholderWithId
        ] === true
      ) {
        processReplacement(target.parentNode);
      }
    });
  }

@@ -121,8 +141,19 @@
  // in the DOM before the mutation observer is started.
  document.querySelectorAll(replacementsSelector).forEach(processReplacement);

  // Start observing the body element for new children.
  observer.observe(document.body, { childList: true });
  // Start observing the body element for new children and for new changes in
  // Text nodes of elements. We need to track Text nodes because content
  // of the node can be too large, browser will receive not fully loaded chunk
  // and render it as is. At this moment json inside script will be invalid and
  // we need to track new changes to that json (Text node), once it will be
  // fully loaded it will be processed.
  // @ingroup large_chunk
  observer.observe(document.body, {
    childList: true,
    // Without this options characterData will not be triggered inside child nodes.
    subtree: true,
    characterData: true,
  });

  // As soon as the document is loaded, no more replacements will be added.
  // Immediately fetch and process all pending mutations and stop the observer.
+19 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Support module for BigPipe testing.
 */

/**
 * Implements hook_theme().
 *
 * @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testBigPipeLargeContent
 */
function big_pipe_regression_test_theme() {
  return [
    'big_pipe_test_large_content' => [
      'variables' => [],
    ],
  ];
}
+8 −0
Original line number Diff line number Diff line
@@ -11,3 +11,11 @@ big_pipe_regression_test.2802923:
    _controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::regression2802923'
  requirements:
    _access: 'TRUE'

big_pipe_test_large_content:
  path: '/big_pipe_test_large_content'
  defaults:
    _controller: '\Drupal\big_pipe_regression_test\BigPipeRegressionTestController::largeContent'
    _title: 'BigPipe test large content'
  requirements:
    _access: 'TRUE'
+27 −1
Original line number Diff line number Diff line
@@ -32,6 +32,32 @@ public function regression2802923() {
    ];
  }

  /**
   * A page with large content.
   *
   * @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testBigPipeLargeContent
   */
  public function largeContent() {
    return [
      'item1' => [
        '#lazy_builder' => [static::class . '::largeContentBuilder', []],
        '#create_placeholder' => TRUE,
      ],
    ];
  }

  /**
   * Renders large content.
   *
   * @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testBigPipeLargeContent
   */
  public static function largeContentBuilder() {
    return [
      '#theme' => 'big_pipe_test_large_content',
      '#cache' => ['max-age' => 0],
    ];
  }

  /**
   * #lazy_builder callback; builds <time> markup with current time.
   *
@@ -48,7 +74,7 @@ public static function currentTime() {
   * {@inheritdoc}
   */
  public static function trustedCallbacks() {
    return ['currentTime'];
    return ['currentTime', 'largeContentBuilder'];
  }

}
+6 −0
Original line number Diff line number Diff line
<div id="big-pipe-large-content">
    {% for i in 0..130000 %}
        boing
    {% endfor %}
</div>
Loading