Skip to content
Snippets Groups Projects
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
Branches
Tags
12 merge requests!11628Update file MediaLibraryWidget.php,!7564Revert "Issue #3364773 by roshnichordiya, Chris Matthews, thakurnishant_06,...,!5752Issue #3275828 by joachim, quietone, bradjones1, Berdir: document the reason...,!5627Issue #3261805: Field not saved when change of 0 on string start,!5427Issue #3338518: send credentials in ajax if configured in CORS settings.,!5395Issue #3387916 by fjgarlin, Spokje: Each GitLab job exposes user email,!5217Issue #3386607 by alexpott: Improve spell checking in commit-code-check.sh,!5064Issue #3379522 by finnsky, Gauravvvv, kostyashupenko, smustgrave, Chi: Revert...,!5040SDC ComponentElement: Transform slots scalar values to #plain_text instead of throwing an exception,!4894Issue #3280279: Add API to allow sites to opt in to upload SVG images in CKEditor 5,!3106Issue #3017548: "Filtered HTML" text format does not support manual teaser break (<!--break-->),!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links
Pipeline #29353 passed
Pipeline: drupal

#29361

    Pipeline: drupal

    #29360

      Pipeline: drupal

      #29359

        +1
        ......@@ -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.
        ......
        <?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' => [],
        ],
        ];
        }
        ......@@ -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'
        ......@@ -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'];
        }
        }
        <div id="big-pipe-large-content">
        {% for i in 0..130000 %}
        boing
        {% endfor %}
        </div>
        ......@@ -124,4 +124,28 @@ public function testPlaceholderInParagraph_2802923() {
        $this->assertJsCondition('document.querySelectorAll(\'p\').length === 1');
        }
        /**
        * Tests BigPipe large content.
        *
        * Repeat loading of same page for two times, after second time the page is
        * cached and the bug consistently reproducible.
        */
        public function testBigPipeLargeContent() {
        $user = $this->drupalCreateUser();
        $this->drupalLogin($user);
        $assert_session = $this->assertSession();
        $this->drupalGet(Url::fromRoute('big_pipe_test_large_content'));
        $this->assertNotNull($assert_session->waitForElement('css', 'script[data-big-pipe-event="stop"]'));
        $this->assertCount(0, $this->getDrupalSettings()['bigPipePlaceholderIds']);
        $this->assertCount(2, $this->getSession()->getPage()->findAll('css', 'script[data-big-pipe-replacement-for-placeholder-with-id]'));
        $assert_session->elementExists('css', '#big-pipe-large-content');
        $this->drupalGet(Url::fromRoute('big_pipe_test_large_content'));
        $this->assertNotNull($assert_session->waitForElement('css', 'script[data-big-pipe-event="stop"]'));
        $this->assertCount(0, $this->getDrupalSettings()['bigPipePlaceholderIds']);
        $this->assertCount(2, $this->getSession()->getPage()->findAll('css', 'script[data-big-pipe-replacement-for-placeholder-with-id]'));
        $assert_session->elementExists('css', '#big-pipe-large-content');
        }
        }
        0% Loading or .
        You are about to add 0 people to the discussion. Proceed with caution.
        Please register or to comment