From 500027aeb27e3f7ac456647558157751bbc1707b Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Sun, 7 Apr 2019 09:41:59 +0900
Subject: [PATCH] Issue #3046243 by tim.plunkett, jibran, dpi: Regression:
 Optional context values may throw exceptions if unsatisfied

(cherry picked from commit 2cceb4a9029009140e34e6597b6e5e76cceab2d0)
---
 .../Core/Plugin/Context/ContextHandler.php    | 30 +++++++++++++++----
 .../Tests/Core/Plugin/ContextHandlerTest.php  |  9 +++---
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
index c4a41b8e917a..587fe66b1f9a 100644
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Component\Plugin\Exception\MissingValueContextException;
+use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 
@@ -110,19 +111,36 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
           // Collect required contexts that exist but are missing a value.
           $missing_value[] = $plugin_context_id;
         }
+
+        // Proceed to the next definition.
+        continue;
+      }
+
+      try {
+        $context = $plugin->getContext($context_id);
+      }
+      catch (ContextException $e) {
+        $context = NULL;
+      }
+      // @todo Remove in https://www.drupal.org/project/drupal/issues/3046342.
+      catch (PluginException $e) {
+        $context = NULL;
       }
-      elseif (($context = $plugin->getContext($context_id)) && $context->hasContextValue()) {
+
+      if ($context && $context->hasContextValue()) {
         // Ignore mappings if the plugin has a value for a missing context.
         unset($mappings[$plugin_context_id]);
+        continue;
       }
-      elseif ($plugin_context_definition->isRequired()) {
+
+      if ($plugin_context_definition->isRequired()) {
         // Collect required contexts that are missing.
         $missing_value[] = $plugin_context_id;
+        continue;
       }
-      else {
-        // Ignore mappings for optional missing context.
-        unset($mappings[$plugin_context_id]);
-      }
+
+      // Ignore mappings for optional missing context.
+      unset($mappings[$plugin_context_id]);
     }
 
     // If there are any mappings that were not satisfied, throw an exception.
diff --git a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
index 05935e11fe8b..115a8c83f49b 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
@@ -18,7 +18,6 @@
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\Plugin\Context\ContextHandler;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
@@ -379,9 +378,9 @@ public function testApplyContextMappingMissingRequired() {
       ->method('setContext');
 
     // No context, so no cacheability metadata can be passed along.
-    $plugin->expects($this->once())
+    $plugin->expects($this->any())
       ->method('getContext')
-      ->willReturn(new Context($context_definition));
+      ->willThrowException(new ContextException());
 
     $this->setExpectedException(MissingValueContextException::class, 'Required contexts without a value: hit');
     $this->contextHandler->applyContextMapping($plugin, $contexts);
@@ -415,9 +414,9 @@ public function testApplyContextMappingMissingNotRequired() {
       ->method('setContext');
 
     // No context, so no cacheability metadata can be passed along.
-    $plugin->expects($this->once())
+    $plugin->expects($this->any())
       ->method('getContext')
-      ->willReturn(new Context($context_definition));
+      ->willThrowException(new ContextException());
 
     $this->contextHandler->applyContextMapping($plugin, $contexts);
   }
-- 
GitLab