diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index e7fb7c2c8254b01d5bf18a931c74d15bb4c3c07c..ab6b9440652475eb206b405d18d5acaaa9f3f9f2 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -307,6 +307,11 @@ block_settings:
     provider:
       type: string
       label: 'Provider'
+    context_mapping:
+      type: sequence
+      label: 'Context assignments'
+      sequence:
+        type: string
 
 condition.plugin:
   type: mapping
diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index 2544254586d6ed388eeaa21f738ba2fc759bba71..5684e3d1c79d4b1f541c693a922f826bf27994dc 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -10,6 +10,7 @@
 use Drupal\block\BlockInterface;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
 use Drupal\Core\Plugin\ContextAwarePluginBase;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\NestedArray;
@@ -28,6 +29,8 @@
  */
 abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
 
+  use ContextAwarePluginAssignmentTrait;
+
   /**
    * The transliteration service.
    *
@@ -175,6 +178,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
       '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE,
     );
 
+    // Add context mapping UI form elements.
+    $contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
+    $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
     // Add plugin-specific settings for this block type.
     $form += $this->blockForm($form, $form_state);
     return $form;
diff --git a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
index 19d756ce8443534aeeb361deefb8e1398bd69468..f9e349ed69e0a035b55616b4dd2ec9b8645da090 100644
--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -56,11 +56,12 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
       if (count($options) > 1) {
         $assignments = $plugin->getContextMapping();
         $element[$context_slot] = [
-          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#title' => $definition->getLabel() ?: $this->t('Select a @context value:', ['@context' => $context_slot]),
           '#type' => 'select',
           '#options' => $options,
           '#required' => $definition->isRequired(),
           '#default_value' => !empty($assignments[$context_slot]) ? $assignments[$context_slot] : '',
+          '#description' => $definition->getDescription(),
         ];
       }
     }
diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php
index 8c13bbbe8a1db63a6ac76f04869bd18cbfa33a34..50e4d38e95cd3beae45fc12e3d61928b85eb29b4 100644
--- a/core/modules/block/src/BlockForm.php
+++ b/core/modules/block/src/BlockForm.php
@@ -335,6 +335,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
 
     // Call the plugin submit handler.
     $entity->getPlugin()->submitConfigurationForm($form, $settings);
+    $block = $entity->getPlugin();
+    // If this block is context-aware, set the context mapping.
+    if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
+      $context_mapping = $settings->getValue('context_mapping', []);
+      $block->setContextMapping($context_mapping);
+    }
     // Update the original form values.
     $form_state->setValue('settings', $settings->getValues());
 
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index 9a582352343ef6f179adc3229fcd3d395b2e2857..7972bbcaf874bf0c863aff9b8676d92ede724e02 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Render\Element;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -139,6 +140,12 @@ protected static function buildPreRenderableBlock($entity, ModuleHandlerInterfac
     $derivative_id = $plugin->getDerivativeId();
     $configuration = $plugin->getConfiguration();
 
+    // Inject runtime contexts.
+    if ($plugin instanceof ContextAwarePluginInterface) {
+      $contexts = \Drupal::service('context.repository')->getRuntimeContexts($plugin->getContextMapping());
+      \Drupal::service('context.handler')->applyContextMapping($plugin, $contexts);
+    }
+
     // Create the render array for the block as a whole.
     // @see template_preprocess_block().
     $build = [
diff --git a/core/modules/block/src/Controller/BlockLibraryController.php b/core/modules/block/src/Controller/BlockLibraryController.php
index 1dcd15defe86a0a16f4693e7f56ea901b8c6f2e5..0f6c96546fd46c8754b655b07a2ffd934d69d31f 100644
--- a/core/modules/block/src/Controller/BlockLibraryController.php
+++ b/core/modules/block/src/Controller/BlockLibraryController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
 use Drupal\Core\Menu\LocalActionManagerInterface;
+use Drupal\Core\Plugin\Context\LazyContextRepository;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -29,6 +30,13 @@ class BlockLibraryController extends ControllerBase {
    */
   protected $blockManager;
 
+  /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\LazyContextRepository
+   */
+  protected $contextRepository;
+
   /**
    * The route match.
    *
@@ -48,15 +56,18 @@ class BlockLibraryController extends ControllerBase {
    *
    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
    *   The block manager.
+   * @param \Drupal\Core\Plugin\Context\LazyContextRepository $context_repository
+   *   The context repository.
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
    *   The current route match.
    * @param \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager
    *   The local action manager.
    */
-  public function __construct(BlockManagerInterface $block_manager, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager) {
+  public function __construct(BlockManagerInterface $block_manager, LazyContextRepository $context_repository, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager) {
     $this->blockManager = $block_manager;
     $this->routeMatch = $route_match;
     $this->localActionManager = $local_action_manager;
+    $this->contextRepository = $context_repository;
   }
 
   /**
@@ -65,6 +76,7 @@ public function __construct(BlockManagerInterface $block_manager, RouteMatchInte
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('plugin.manager.block'),
+      $container->get('context.repository'),
       $container->get('current_route_match'),
       $container->get('plugin.manager.menu.local_action')
     );
@@ -95,7 +107,7 @@ public function listBlocks(Request $request, $theme) {
     ];
 
     // Only add blocks which work without any available context.
-    $definitions = $this->blockManager->getDefinitionsForContexts();
+    $definitions = $this->blockManager->getDefinitionsForContexts($this->contextRepository->getAvailableContexts());
     // Order by category, and then by admin label.
     $definitions = $this->blockManager->getSortedDefinitions($definitions);
 
diff --git a/core/modules/block/src/Tests/BlockInterfaceTest.php b/core/modules/block/src/Tests/BlockInterfaceTest.php
index 28208ac93361df339f74fc8a5569448f508b0a8a..5af8ec0dc74d5bc37b0d99a196fcb21f95597af2 100644
--- a/core/modules/block/src/Tests/BlockInterfaceTest.php
+++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
@@ -79,6 +79,7 @@ public function testBlockInterface() {
         '#default_value' => TRUE,
         '#return_value' => 'visible',
       ),
+      'context_mapping' => array(),
       'display_message' => array(
         '#type' => 'textfield',
         '#title' => t('Display message'),
diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php
index 721a7e2e518cacc8598eb7c86f466a9689b886a7..532094265726dddffc5edc2e2046509fc79f14ee 100644
--- a/core/modules/block/src/Tests/BlockUiTest.php
+++ b/core/modules/block/src/Tests/BlockUiTest.php
@@ -165,22 +165,58 @@ public function testCandidateBlockList() {
     $this->assertTrue(!empty($elements), 'The test block appears in a custom category controlled by block_test_block_alter().');
   }
 
+  /**
+   * Tests the behavior of unsatisfied context-aware blocks.
+   */
+  public function testContextAwareUnsatisfiedBlocks() {
+    $arguments = array(
+      ':category' => 'Block test',
+      ':href' => 'admin/structure/block/add/test_context_aware_unsatisfied/classy',
+      ':text' => 'Test context-aware unsatisfied block',
+    );
+
+    $this->drupalGet('admin/structure/block');
+    $this->clickLinkPartialName('Place block');
+    $elements = $this->xpath('//tr[.//td/div[text()=:text] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]', $arguments);
+    $this->assertTrue(empty($elements), 'The context-aware test block does not appear.');
+
+    $definition = \Drupal::service('plugin.manager.block')->getDefinition('test_context_aware_unsatisfied');
+    $this->assertTrue(!empty($definition), 'The context-aware test block does not exist.');
+  }
+
   /**
    * Tests the behavior of context-aware blocks.
    */
   public function testContextAwareBlocks() {
+    $expected_text = '<div id="test_context_aware--username">' . \Drupal::currentUser()->getUsername() . '</div>';
+    $this->drupalGet('');
+    $this->assertNoText('Test context-aware block');
+    $this->assertNoRaw($expected_text);
+
+    $block_url = 'admin/structure/block/add/test_context_aware/classy';
     $arguments = array(
-      ':ul_class' => 'block-list',
-      ':li_class' => 'test-context-aware',
-      ':href' => 'admin/structure/block/add/test_context_aware/classy',
-      ':text' => 'Test context-aware block',
+      ':title' => 'Test context-aware block',
+      ':category' => 'Block test',
+      ':href' => $block_url,
     );
+    $pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]';
 
     $this->drupalGet('admin/structure/block');
-    $elements = $this->xpath('//details[@id="edit-category-block-test"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
-    $this->assertTrue(empty($elements), 'The context-aware test block does not appear.');
+    $this->clickLinkPartialName('Place block');
+    $elements = $this->xpath($pattern, $arguments);
+    $this->assertTrue(!empty($elements), 'The context-aware test block appears.');
     $definition = \Drupal::service('plugin.manager.block')->getDefinition('test_context_aware');
     $this->assertTrue(!empty($definition), 'The context-aware test block exists.');
+
+    $edit = [
+      'region' => 'content',
+      'settings[context_mapping][user]' => '@block_test.multiple_static_context:user2',
+    ];
+    $this->drupalPostForm($block_url, $edit, 'Save block');
+
+    $this->drupalGet('');
+    $this->assertText('Test context-aware block');
+    $this->assertRaw($expected_text);
   }
 
   /**
diff --git a/core/modules/block/tests/modules/block_test/block_test.services.yml b/core/modules/block/tests/modules/block_test/block_test.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f165a02470fc4e2f4b39a434cbbc599019c1b8b3
--- /dev/null
+++ b/core/modules/block/tests/modules/block_test/block_test.services.yml
@@ -0,0 +1,6 @@
+services:
+  block_test.multiple_static_context:
+    class: Drupal\block_test\ContextProvider\MultipleStaticContext
+    arguments: ['@current_user', '@entity.manager']
+    tags:
+      - { name: 'context_provider' }
diff --git a/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php b/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php
new file mode 100644
index 0000000000000000000000000000000000000000..fea7f6d9f167927ba3024f1a515f87c14aec1a8f
--- /dev/null
+++ b/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_test\ContextProvider\MultipleStaticContext.
+ */
+
+namespace Drupal\block_test\ContextProvider;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\ContextProviderInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Sets multiple contexts for a static value.
+ */
+class MultipleStaticContext implements ContextProviderInterface {
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * The user storage.
+   *
+   * @var \Drupal\user\UserStorageInterface
+   */
+  protected $userStorage;
+
+  /**
+   * Constructs a new MultipleStaticContext.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(AccountInterface $account, EntityManagerInterface $entity_manager) {
+    $this->account = $account;
+    $this->userStorage = $entity_manager->getStorage('user');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRuntimeContexts(array $unqualified_context_ids) {
+    $current_user = $this->userStorage->load($this->account->id());
+
+    $context1 = new Context(new ContextDefinition('entity:user', 'User 1'));
+    $context1->setContextValue($current_user);
+
+    $context2 = new Context(new ContextDefinition('entity:user', 'User 2'));
+    $context2->setContextValue($current_user);
+
+    $cacheability = new CacheableMetadata();
+    $cacheability->setCacheContexts(['user']);
+
+    $context1->addCacheableDependency($cacheability);
+    $context2->addCacheableDependency($cacheability);
+
+    return [
+      'user1' => $context1,
+      'user2' => $context2,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableContexts() {
+    return $this->getRuntimeContexts([]);
+  }
+
+}
diff --git a/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php b/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php
index 56b127b13fcf4eda1a96739a5b7d99ec55ac3829..c4223d0ea79f94b7e52cf37c28596ea4cfcb66fd 100644
--- a/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php
+++ b/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php
@@ -29,6 +29,8 @@ public function build() {
     /** @var $user \Drupal\user\UserInterface */
     $user = $this->getContextValue('user');
     return array(
+      '#prefix' => '<div id="' . $this->getPluginId() . '--username">',
+      '#suffix' => '</div>',
       '#markup' => $user->getUsername(),
     );
   }
diff --git a/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareUnsatisfiedBlock.php b/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareUnsatisfiedBlock.php
new file mode 100644
index 0000000000000000000000000000000000000000..7232e745a9babbec272e11556ee542c05e497c61
--- /dev/null
+++ b/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareUnsatisfiedBlock.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_test\Plugin\Block\TestContextAwareBlock.
+ */
+
+namespace Drupal\block_test\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+
+/**
+ * Provides a context-aware block.
+ *
+ * @Block(
+ *   id = "test_context_aware_unsatisfied",
+ *   admin_label = @Translation("Test context-aware unsatisfied block"),
+ *   context = {
+ *     "user" = @ContextDefinition("entity:foobar")
+ *   }
+ * )
+ */
+class TestContextAwareUnsatisfiedBlock extends BlockBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return [
+      '#markup' => 'test',
+    ];
+  }
+
+}
diff --git a/core/modules/node/src/ContextProvider/NodeRouteContext.php b/core/modules/node/src/ContextProvider/NodeRouteContext.php
index e6f5781ce79f8cbe19877041a47318a9719bd6b9..6f55b08567c7348c9216a1b43c42b573a359c4d2 100644
--- a/core/modules/node/src/ContextProvider/NodeRouteContext.php
+++ b/core/modules/node/src/ContextProvider/NodeRouteContext.php
@@ -13,12 +13,15 @@
 use Drupal\Core\Plugin\Context\ContextProviderInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\node\Entity\Node;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 
 /**
  * Sets the current node as a context on node routes.
  */
 class NodeRouteContext implements ContextProviderInterface {
 
+  use StringTranslationTrait;
+
   /**
    * The route match object.
    *
@@ -63,7 +66,7 @@ public function getRuntimeContexts(array $unqualified_context_ids) {
    * {@inheritdoc}
    */
   public function getAvailableContexts() {
-    $context = new Context(new ContextDefinition('entity:node'));
+    $context = new Context(new ContextDefinition('entity:node', $this->t('Node from URL')));
     return ['node' => $context];
   }