Skip to content
Snippets Groups Projects
Verified Commit 2172d100 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3379885 by catch, Wim Leers: Use MessagesCommand in BigPipe to remove...

Issue #3379885 by catch, Wim Leers: Use MessagesCommand in BigPipe to remove special casing of the messages placeholder

(cherry picked from commit 44c345dd)
parent 0d4a5883
Branches
Tags
28 merge requests!11958Issue #3490507 by alexpott, smustgrave: Fix bogus mocking in...,!11769Issue #3517987: Add option to contextual filters to encode slashes in query parameter.,!11185Issue #3477324 by andypost, alexpott: Fix usage of str_getcsv() and fgetcsv() for PHP 8.4,!10602Issue #3438769 by vinmayiswamy, antonnavi, michelle, amateescu: Sub workspace does not clear,!10301Issue #3469309 by mstrelan, smustgrave, moshe weitzman: Use one-time login...,!10187Issue #3487488 by dakwamine: ExtensionMimeTypeGuesser::guessMimeType must support file names with "0" (zero) like foo.0.zip,!9944Issue #3483353: Consider making the createCopy config action optionally fail...,!9929Issue #3445469 by pooja_sharma, smustgrave: Add additional test coverage for...,!9787Resolve issue 3479427 - bootstrap barrio issue under Windows,!9742Issue #3463908 by catch, quietone: Split OptionsFieldUiTest into two,!9526Issue #3458177 by mondrake, catch, quietone, godotislate, longwave, larowlan,...,!8738Issue #3424162 by camilledavis, dineshkumarbollu, smustgrave: Claro...,!8704Make greek characters available in ckeditor5,!8597Draft: Issue #3442259 by catch, quietone, dww: Reduce time of Migrate Upgrade tests...,!8533Issue #3446962 by kim.pepper: Remove incorrectly added...,!8517Issue #3443748 by NexusNovaz, smustgrave: Testcase creates false positive,!8325Update file Sort.php,!8095Expose document root on install,!7930Resolve #3427374 "Taxonomytid viewsargumentdefault plugin",!7627Issue #3439440 by nicxvan, Binoli Lalani, longwave: Remove country support from DateFormatter,!7445Issue #3440169: When using drupalGet(), provide an associative array for $headers,!7401#3271894 Fix documented StreamWrapperInterface return types for realpath() and dirname(),!7384Add constraints to system.advisories,!7078Issue #3320569 by Spokje, mondrake, smustgrave, longwave, quietone, Lendude,...,!6622Issue #2559833 by piggito, mohit_aghera, larowlan, guptahemant, vakulrai,...,!6502Draft: Resolve #2938524 "Plach testing issue",!38582585169-10.1.x,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key
Pipeline #101266 passed with warnings
Pipeline: drupal

#101282

    Pipeline: drupal

    #101273

      ......@@ -11,7 +11,7 @@ services:
      - { name: placeholder_strategy, priority: 0 }
      big_pipe:
      class: Drupal\big_pipe\Render\BigPipe
      arguments: ['@renderer', '@session', '@request_stack', '@http_kernel', '@event_dispatcher', '@config.factory']
      arguments: ['@renderer', '@session', '@request_stack', '@http_kernel', '@event_dispatcher', '@config.factory', '@messenger']
      Drupal\big_pipe\Render\BigPipe: '@big_pipe'
      html_response.attachments_processor.big_pipe:
      public: false
      ......
      ......@@ -5,10 +5,12 @@
      use Drupal\Component\Utility\Crypt;
      use Drupal\Component\Utility\Html;
      use Drupal\Core\Ajax\AjaxResponse;
      use Drupal\Core\Ajax\MessageCommand;
      use Drupal\Core\Ajax\ReplaceCommand;
      use Drupal\Core\Asset\AttachedAssets;
      use Drupal\Core\Asset\AttachedAssetsInterface;
      use Drupal\Core\Config\ConfigFactoryInterface;
      use Drupal\Core\Messenger\MessengerInterface;
      use Drupal\Core\Render\HtmlResponse;
      use Drupal\Core\Render\RendererInterface;
      use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
      ......@@ -205,29 +207,17 @@ class BigPipe {
      */
      protected $configFactory;
      /**
      * Constructs a new BigPipe class.
      *
      * @param \Drupal\Core\Render\RendererInterface $renderer
      * The renderer.
      * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
      * The session.
      * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
      * The request stack.
      * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
      * The HTTP kernel.
      * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $event_dispatcher
      * The event dispatcher.
      * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
      * The config factory.
      */
      public function __construct(RendererInterface $renderer, SessionInterface $session, RequestStack $request_stack, HttpKernelInterface $http_kernel, EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory) {
      public function __construct(RendererInterface $renderer, SessionInterface $session, RequestStack $request_stack, HttpKernelInterface $http_kernel, EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory, protected ?MessengerInterface $messenger = NULL) {
      $this->renderer = $renderer;
      $this->session = $session;
      $this->requestStack = $request_stack;
      $this->httpKernel = $http_kernel;
      $this->eventDispatcher = $event_dispatcher;
      $this->configFactory = $config_factory;
      if (!isset($this->messenger)) {
      @trigger_error('Calling ' . __CLASS__ . '::_construct() without the $messenger argument is deprecated in drupal:10.3.0 and it will be required in drupal:11.0.0. See https://www.drupal.org/node/3343754', E_USER_DEPRECATED);
      $this->messenger = \Drupal::messenger();
      }
      }
      /**
      ......@@ -538,29 +528,16 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
      // Create a Fiber for each placeholder.
      $fibers = [];
      $message_placeholder_id = NULL;
      foreach ($placeholder_order as $placeholder_id) {
      if (!isset($placeholders[$placeholder_id])) {
      continue;
      }
      $placeholder_render_array = $placeholders[$placeholder_id];
      // Ensure the messages placeholder renders last, the render order of every
      // other placeholder is safe to change.
      // @see static::getPlaceholderOrder()
      if (isset($placeholder_render_array['#lazy_builder']) && $placeholder_render_array['#lazy_builder'][0] === 'Drupal\Core\Render\Element\StatusMessages::renderMessages') {
      $message_placeholder_id = $placeholder_id;
      }
      $fibers[$placeholder_id] = new \Fiber(fn() => $this->renderPlaceholder($placeholder_id, $placeholder_render_array));
      }
      $iterations = 0;
      while (count($fibers) > 0) {
      foreach ($fibers as $placeholder_id => $fiber) {
      // Keep skipping the messages placeholder until it's the only Fiber
      // remaining. @todo https://www.drupal.org/project/drupal/issues/3379885
      if (isset($message_placeholder_id) && $placeholder_id === $message_placeholder_id && count($fibers) > 1) {
      continue;
      }
      try {
      if (!$fiber->isStarted()) {
      $fiber->start();
      ......@@ -594,6 +571,15 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
      $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup']));
      $ajax_response->setAttachments($elements['#attached']);
      // Delete all messages that were generated during the rendering of this
      // placeholder, to render them in a BigPipe-optimized way.
      $messages = $this->messenger->deleteAll();
      foreach ($messages as $type => $type_messages) {
      foreach ($type_messages as $message) {
      $ajax_response->addCommand(new MessageCommand($message, NULL, ['type' => $type], FALSE));
      }
      }
      // Push a fake request with the asset libraries loaded so far and
      // dispatch KernelEvents::RESPONSE event. This results in the
      // attachments for the AJAX response being processed by
      ......@@ -736,8 +722,7 @@ protected function renderPlaceholder($placeholder, array $placeholder_render_arr
      *
      * @return array
      * Indexed array; the order in which the BigPipe placeholders will start
      * execution. Placeholders begin execution in DOM order, except for the
      * messages placeholder which must always be executed last. Note that due to
      * execution. Placeholders begin execution in DOM order. Note that due to
      * the Fibers implementation of BigPipe, although placeholders will start
      * executing in DOM order, they may finish and render in any order. Values
      * are the BigPipe placeholder IDs. Note that only unique placeholders are
      ......@@ -751,35 +736,7 @@ protected function getPlaceholderOrder($html, $placeholders) {
      foreach ($xpath->query('//span[@data-big-pipe-placeholder-id]') as $node) {
      $placeholder_ids[] = Html::escape($node->getAttribute('data-big-pipe-placeholder-id'));
      }
      $placeholder_ids = array_unique($placeholder_ids);
      // The 'status messages' placeholder needs to be special cased, because it
      // depends on global state that can be modified when other placeholders are
      // being rendered: any code can add messages to render.
      // This violates the principle that each lazy builder must be able to render
      // itself in isolation, and therefore in any order. However, we cannot
      // change the way \Drupal\Core\Messenger\MessengerInterface::addMessage()
      // works in the Drupal 8 cycle. So we have to accommodate its special needs.
      // Allowing placeholders to be rendered in a particular order (in this case:
      // last) would violate this isolation principle. Thus a monopoly is granted
      // to this one special case, with this hard-coded solution.
      // @see \Drupal\Core\Render\Element\StatusMessages
      // @see \Drupal\Core\Render\Renderer::replacePlaceholders()
      // @see https://www.drupal.org/node/2712935#comment-11368923
      $message_placeholder_ids = [];
      foreach ($placeholders as $placeholder_id => $placeholder_element) {
      if (isset($placeholder_element['#lazy_builder']) && $placeholder_element['#lazy_builder'][0] === 'Drupal\Core\Render\Element\StatusMessages::renderMessages') {
      $message_placeholder_ids[] = $placeholder_id;
      }
      }
      // Return placeholder IDs in DOM order, but with the 'status messages'
      // placeholders at the end, if they are present.
      $ordered_placeholder_ids = array_merge(
      array_diff($placeholder_ids, $message_placeholder_ids),
      array_intersect($placeholder_ids, $message_placeholder_ids)
      );
      return $ordered_placeholder_ids;
      return array_unique($placeholder_ids);
      }
      /**
      ......
      ......@@ -175,14 +175,12 @@ public function testBigPipe() {
      $cases['exception__lazy_builder']->bigPipePlaceholderId => NULL,
      $cases['exception__embedded_response']->bigPipePlaceholderId => NULL,
      ], [
      0 => $cases['edge_case__html_non_lazy_builder']->bigPipePlaceholderId,
      0 => $cases['html']->bigPipePlaceholderId,
      1 => $cases['edge_case__html_non_lazy_builder']->bigPipePlaceholderId,
      // The suspended placeholder is replaced after the non-suspended
      // placeholder even though it appears first in the page.
      // @see Drupal\big_pipe\Render\BigPipe\Render::sendPlaceholders()
      1 => $cases['edge_case__html_non_lazy_builder_suspend']->bigPipePlaceholderId,
      // The 'html' case contains the 'status messages' placeholder, which is
      // always rendered last.
      2 => $cases['html']->bigPipePlaceholderId,
      2 => $cases['edge_case__html_non_lazy_builder_suspend']->bigPipePlaceholderId,
      ]);
      $this->assertSession()->responseContains('</body>');
      ......
      ......@@ -82,8 +82,7 @@ public function testMessages_2712935() {
      $this->assertTrue($this->container->get('module_installer')->install(['render_placeholder_message_test'], TRUE), 'Installed modules.');
      $this->drupalLogin($this->drupalCreateUser());
      $messages_markup = '<div role="contentinfo" aria-label="Status message"';
      $messages_markup = '<div class="messages messages--status" role="status"';
      $test_routes = [
      // Messages placeholder rendered first.
      'render_placeholder_message_test.first',
      ......@@ -104,9 +103,9 @@ public function testMessages_2712935() {
      $assert->elementContains('css', 'p.logged-message:nth-of-type(1)', 'Message: P1');
      $assert->elementContains('css', 'p.logged-message:nth-of-type(2)', 'Message: P2');
      $assert->responseContains($messages_markup);
      $assert->elementExists('css', 'div[aria-label="Status message"] ul');
      $assert->elementContains('css', 'div[aria-label="Status message"] ul li:nth-of-type(1)', 'P1');
      $assert->elementContains('css', 'div[aria-label="Status message"] ul li:nth-of-type(2)', 'P2');
      $assert->elementExists('css', 'div[aria-label="Status message"]');
      $assert->responseContains('aria-label="Status message">P1');
      $assert->responseContains('aria-label="Status message">P2');
      // Verify that we end with all messages printed, hence again zero queued.
      $this->drupalGet(Url::fromRoute('render_placeholder_message_test.queued'));
      ......
      ......@@ -7,6 +7,7 @@
      use Drupal\big_pipe\Render\BigPipe;
      use Drupal\big_pipe\Render\BigPipeResponse;
      use Drupal\Core\Config\ConfigFactoryInterface;
      use Drupal\Core\Messenger\MessengerInterface;
      use Drupal\Core\Render\ElementInfoManagerInterface;
      use Drupal\Core\Render\HtmlResponse;
      use Drupal\Core\Render\PlaceholderGeneratorInterface;
      ......@@ -54,7 +55,7 @@ public function testLongPlaceholderFiberSuspendingLoop() {
      'languages:language_interface',
      'theme',
      ],
      ]
      ],
      );
      $session = $this->prophesize(SessionInterface::class);
      ......@@ -67,6 +68,7 @@ public function testLongPlaceholderFiberSuspendingLoop() {
      $this->prophesize(HttpKernelInterface::class)->reveal(),
      $this->createMock(EventDispatcherInterface::class),
      $this->prophesize(ConfigFactoryInterface::class)->reveal(),
      $this->prophesize(MessengerInterface::class)->reveal(),
      );
      $response = new BigPipeResponse(new HtmlResponse());
      ......
      ......@@ -7,6 +7,7 @@
      use Drupal\big_pipe\Render\BigPipe;
      use Drupal\big_pipe\Render\BigPipeResponse;
      use Drupal\Core\Config\ConfigFactoryInterface;
      use Drupal\Core\Messenger\MessengerInterface;
      use Drupal\Core\Render\HtmlResponse;
      use Drupal\Core\Render\RendererInterface;
      use Drupal\Tests\UnitTestCase;
      ......@@ -34,7 +35,8 @@ public function testManyNoJsPlaceHolders() {
      $this->prophesize(RequestStack::class)->reveal(),
      $this->prophesize(HttpKernelInterface::class)->reveal(),
      $this->prophesize(EventDispatcherInterface::class)->reveal(),
      $this->prophesize(ConfigFactoryInterface::class)->reveal()
      $this->prophesize(ConfigFactoryInterface::class)->reveal(),
      $this->prophesize(MessengerInterface::class)->reveal()
      );
      $response = new BigPipeResponse(new HtmlResponse());
      ......
      0% Loading or .
      You are about to add 0 people to the discussion. Proceed with caution.
      Please register or to comment