2c2 < index 3e67737..6f32c6c 100644 --- > index 3e67737..e7f5377 100644 5,6c5,17 < @@ -339,6 +339,9 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde < return; --- > @@ -508,7 +508,9 @@ protected function renderPlaceholder($placeholder, array $placeholder_render_arr > * > * @return array > * Indexed array; the order in which the BigPipe placeholders must be sent. > - * Values are the BigPipe placeholder IDs. > + * Values are the BigPipe placeholder IDs. Note that only unique > + * placeholders are kept: if the same placeholder occurs multiple times, we > + * only keep the first occurrence. > */ > protected function getPlaceholderOrder($html) { > $fragments = explode('
'; > + $raw_content = $this->getRawContent(); > + $this->assertTrue(substr_count($raw_content, $expected_placeholder_replacement) == 1, 'Only one placeholder replacement was found for the duplicate #lazy_builder arrays.'); > + } 12,31c54,79 < // Send the start signal. < print "\n"; < print static::START_SIGNAL; < @@ -382,7 +385,17 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde < // placeholder ID (which has HTML entities encoded since we use it to find < // the placeholders). < $big_pipe_js_placeholder_id = Html::decodeEntities($placeholder_id); < - $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup'])); < + < + // If there are identical placeholders, only add the ReplaceCommand once, < + // because all of the placeholders will be replaced the first time the < + // ReplaceCommand runs. Otherwise, Drupal.detachBehaviors() and < + // Drupal.attachBehaviors() will run on the context of the entire < + // document for subsequent ReplaceCommand instances, because all < + // placeholders with the identical ID will have been removed from the DOM < + // and their context will no longer be available to JavaScript. < + if (!in_array($big_pipe_js_placeholder_id, $sent_placeholders)) { < + $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup'])); < + } < $ajax_response->setAttachments($elements['#attached']); --- > protected function assertBigPipeResponseHeadersPresent() { > $this->pass('Verifying BigPipe response headers…', 'Debug'); > $this->assertTrue(FALSE !== strpos($this->drupalGetHeader('Cache-Control'), 'private'), 'Cache-Control header set to "private".'); > diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml > index 4979406..e60481c 100644 > --- a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml > +++ b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml > @@ -15,3 +15,12 @@ no_big_pipe: > _no_big_pipe: TRUE > requirements: > _access: 'TRUE' > + > +big_pipe_duplicate_placeholders: > + path: '/big_pipe_duplicate_placeholders' > + defaults: > + _controller: '\Drupal\big_pipe_test\BigPipeTestController::duplicates' > + _title: 'BigPipe with duplicate placeholders test' > + requirements: > + _access: 'TRUE' > + > diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php > index 450a464..1327f37 100644 > --- a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php > +++ b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php > @@ -53,6 +53,30 @@ public static function nope() { > } 33,36c81,111 < // Push a fake request with the asset libraries loaded so far and dispatch < @@ -418,6 +431,9 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde < print $output; < flush(); --- > /** > + * Returns a render array with duplicate lazy_builder placeholders to test > + * BigPipe handling of duplicates. > + * > + * @return array > + */ > + public function duplicates() { > + $adjective = 'awesome'; > + return [ > + 'item1' => [ > + '#lazy_builder' => [static::class . '::placeholderContent', [$adjective]], > + '#create_placeholder' => TRUE, > + ], > + 'item2' => [ > + '#lazy_builder' => [static::class . '::placeholderContent', [$adjective]], > + '#create_placeholder' => TRUE, > + ], > + 'item3' => [ > + '#lazy_builder' => [static::class . '::placeholderContent', [$adjective]], > + '#create_placeholder' => TRUE, > + ], > + ]; > + } > + > + /** > * #lazy_builder callback; builds