diff --git a/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
index e820f0c..c1bb0e9 100644
--- a/core/lib/Drupal/Core/Annotation/ContextDefinition.php
+++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
@@ -113,7 +113,7 @@ public function __construct(array $values) {
     // used in the classes they pass to.
     foreach (['label', 'description'] as $key) {
       // @todo Remove this workaround in https://www.drupal.org/node/2362727.
-      if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
+      if (isset($values[$key]) && $values[$key] instanceof Translation && $values[$key]->get() instanceof TranslationWrapper) {
         $values[$key] = (string) $values[$key]->get();
       }
       else {
diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index 8c97809..7b6bcef 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Cache\CacheableDependencyInterface;
 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;
@@ -30,6 +31,8 @@
  */
 abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
 
+  use ContextAwarePluginAssignmentTrait;
+
   /**
    * The transliteration service.
    *
@@ -198,6 +201,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
       '#options' => $period,
     );
 
+    // Add context 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/Block/BlockBase.php.orig b/core/lib/Drupal/Core/Block/BlockBase.php.orig
new file mode 100644
index 0000000..8c97809
--- /dev/null
+++ b/core/lib/Drupal/Core/Block/BlockBase.php.orig
@@ -0,0 +1,307 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Block\BlockBase.
+ */
+
+namespace Drupal\Core\Block;
+
+use Drupal\block\BlockInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContextAwarePluginBase;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Component\Transliteration\TransliterationInterface;
+
+/**
+ * Defines a base block implementation that most blocks plugins will extend.
+ *
+ * This abstract class provides the generic block configuration form, default
+ * block settings, and handling for general user-defined block visibility
+ * settings.
+ *
+ * @ingroup block_api
+ */
+abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
+
+  /**
+   * The transliteration service.
+   *
+   * @var \Drupal\Component\Transliteration\TransliterationInterface
+   */
+  protected $transliteration;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label() {
+    if (!empty($this->configuration['label'])) {
+      return $this->configuration['label'];
+    }
+
+    $definition = $this->getPluginDefinition();
+    // Cast the admin label to a string since it is an object.
+    // @see \Drupal\Core\StringTranslation\TranslationWrapper
+    return (string) $definition['admin_label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = NestedArray::mergeDeep(
+      $this->baseConfigurationDefaults(),
+      $this->defaultConfiguration(),
+      $configuration
+    );
+  }
+
+  /**
+   * Returns generic default configuration for block plugins.
+   *
+   * @return array
+   *   An associative array with the default configuration.
+   */
+  protected function baseConfigurationDefaults() {
+    return array(
+      'id' => $this->getPluginId(),
+      'label' => '',
+      'provider' => $this->pluginDefinition['provider'],
+      'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
+      'cache' => array(
+        // Blocks are cacheable by default.
+        'max_age' => Cache::PERMANENT,
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfigurationValue($key, $value) {
+    $this->configuration[$key] = $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account, $return_as_object = FALSE) {
+    $access = $this->blockAccess($account);
+    return $return_as_object ? $access : $access->isAllowed();
+  }
+
+  /**
+   * Indicates whether the block should be shown.
+   *
+   * Blocks with specific access checking should override this method rather
+   * than access(), in order to avoid repeating the handling of the
+   * $return_as_object argument.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user session for which to check access.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   *   The access result.
+   *
+   * @see self::access()
+   */
+  protected function blockAccess(AccountInterface $account) {
+    // By default, the block is visible.
+    return AccessResult::allowed();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Creates a generic configuration form for all block types. Individual
+   * block plugins can add elements to this form by overriding
+   * BlockBase::blockForm(). Most block plugins should not override this
+   * method unless they need to alter the generic form elements.
+   *
+   * @see \Drupal\Core\Block\BlockBase::blockForm()
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $definition = $this->getPluginDefinition();
+    $form['provider'] = array(
+      '#type' => 'value',
+      '#value' => $definition['provider'],
+    );
+
+    $form['admin_label'] = array(
+      '#type' => 'item',
+      '#title' => $this->t('Block description'),
+      '#plain_text' => $definition['admin_label'],
+    );
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Title'),
+      '#maxlength' => 255,
+      '#default_value' => $this->label(),
+      '#required' => TRUE,
+    );
+    $form['label_display'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Display title'),
+      '#default_value' => ($this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE),
+      '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE,
+    );
+    // Identical options to the ones for page caching.
+    // @see \Drupal\system\Form\PerformanceForm::buildForm()
+    $period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400);
+    $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period));
+    $period[0] = '<' . $this->t('no caching') . '>';
+    $period[\Drupal\Core\Cache\Cache::PERMANENT] = $this->t('Forever');
+    $form['cache'] = array(
+      '#type' => 'details',
+      '#title' => $this->t('Cache settings'),
+    );
+    $form['cache']['max_age'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Maximum age'),
+      '#description' => $this->t('The maximum time this block may be cached.'),
+      '#default_value' => $this->configuration['cache']['max_age'],
+      '#options' => $period,
+    );
+
+    // Add plugin-specific settings for this block type.
+    $form += $this->blockForm($form, $form_state);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Most block plugins should not override this method. To add validation
+   * for a specific block type, override BlockBase::blockValidate().
+   *
+   * @see \Drupal\Core\Block\BlockBase::blockValidate()
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    // Remove the admin_label form item element value so it will not persist.
+    $form_state->unsetValue('admin_label');
+
+    $this->blockValidate($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockValidate($form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   *
+   * Most block plugins should not override this method. To add submission
+   * handling for a specific block type, override BlockBase::blockSubmit().
+   *
+   * @see \Drupal\Core\Block\BlockBase::blockSubmit()
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    // Process the block's submission handling if no errors occurred only.
+    if (!$form_state->getErrors()) {
+      $this->configuration['label'] = $form_state->getValue('label');
+      $this->configuration['label_display'] = $form_state->getValue('label_display');
+      $this->configuration['provider'] = $form_state->getValue('provider');
+      $this->configuration['cache'] = $form_state->getValue('cache');
+      $this->blockSubmit($form, $form_state);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineNameSuggestion() {
+    $definition = $this->getPluginDefinition();
+    $admin_label = $definition['admin_label'];
+
+    // @todo This is basically the same as what is done in
+    //   \Drupal\system\MachineNameController::transliterate(), so it might make
+    //   sense to provide a common service for the two.
+    $transliterated = $this->transliteration()->transliterate($admin_label, LanguageInterface::LANGCODE_DEFAULT, '_');
+    $transliterated = Unicode::strtolower($transliterated);
+
+    $transliterated = preg_replace('@[^a-z0-9_.]+@', '', $transliterated);
+
+    return $transliterated;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    $max_age = parent::getCacheMaxAge();
+    // @todo Configurability of this will be removed in
+    //   https://www.drupal.org/node/2458763.
+    return Cache::mergeMaxAges($max_age, (int) $this->configuration['cache']['max_age']);
+  }
+
+  /**
+   * Wraps the transliteration service.
+   *
+   * @return \Drupal\Component\Transliteration\TransliterationInterface
+   */
+  protected function transliteration() {
+    if (!$this->transliteration) {
+      $this->transliteration = \Drupal::transliteration();
+    }
+    return $this->transliteration;
+  }
+
+  /**
+   * Sets the transliteration service.
+   *
+   * @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
+   *   The transliteration service.
+   */
+  public function setTransliteration(TransliterationInterface $transliteration) {
+    $this->transliteration = $transliteration;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
index 19d756c..e3ae261 100644
--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -51,16 +51,26 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
           '#type' => 'value',
           '#value' => $context_id,
         ];
+        $element['context_slot_title'] = [
+          '#type' => 'item',
+          '#title' => $definition->getLabel() ?: $context_slot,
+          '#markup' => $options[$context_id],
+        ];
       }
 
       if (count($options) > 1) {
+        // The context display element is only useful when a single context is
+        // available, we have multiple and require user interaction.
+        unset($element['context_slot_title']);
+
         $assignments = $plugin->getContextMapping();
         $element[$context_slot] = [
-          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#title' => $definition->getLabel() ?: $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/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
index 51a39d7..a5154d5 100644
--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
@@ -64,6 +64,10 @@ public function getContextMapping() {
    * {@inheritdoc}
    */
   public function setContextMapping(array $context_mapping) {
+    // The ContextAwarePluginAssignmentTrait adds a visual element to the
+    // context mapping when only one context is available. We should unset this
+    // before saving the context mapping.
+    unset($context_mapping['context_slot_title']);
     if ($this instanceof ConfigurablePluginInterface) {
       $configuration = $this->getConfiguration();
       $configuration['context_mapping'] = $context_mapping;
diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml
index fe20e4a..ff4277d 100644
--- a/core/modules/block/block.services.yml
+++ b/core/modules/block/block.services.yml
@@ -9,4 +9,4 @@ services:
       - { name: event_subscriber }
   block.repository:
     class: Drupal\block\BlockRepository
-    arguments: ['@entity.manager', '@theme.manager', '@context.handler']
+    arguments: ['@entity.manager', '@theme.manager', '@context.handler', '@context.repository']
diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php
index 8c13bbb..e8035f4 100644
--- a/core/modules/block/src/BlockForm.php
+++ b/core/modules/block/src/BlockForm.php
@@ -334,7 +334,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $settings = (new FormState())->setValues($form_state->getValue('settings'));
 
     // Call the plugin submit handler.
-    $entity->getPlugin()->submitConfigurationForm($form, $settings);
+    $block = $entity->getPlugin();
+    $block->submitConfigurationForm($form, $settings);
+    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());
 
@@ -345,7 +350,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $condition_values = (new FormState())
         ->setValues($values);
       $condition->submitConfigurationForm($form, $condition_values);
-      if ($condition instanceof ContextAwarePluginInterface) {
+      if ($condition instanceof ContextAwarePluginInterface && $condition->getContextDefinitions()) {
         $context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
         $condition->setContextMapping($context_mapping);
       }
diff --git a/core/modules/block/src/BlockListBuilder.php b/core/modules/block/src/BlockListBuilder.php
index b92e7ae..e2e4ae7 100644
--- a/core/modules/block/src/BlockListBuilder.php
+++ b/core/modules/block/src/BlockListBuilder.php
@@ -88,7 +88,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_type,
       $container->get('entity.manager')->getStorage($entity_type->id()),
       $container->get('theme.manager'),
-      $container->get('form_builder')
+      $container->get('form_builder'),
+      $container->get('context.repository')
     );
   }
 
diff --git a/core/modules/block/src/BlockListBuilder.php.orig b/core/modules/block/src/BlockListBuilder.php.orig
new file mode 100644
index 0000000..b92e7ae
--- /dev/null
+++ b/core/modules/block/src/BlockListBuilder.php.orig
@@ -0,0 +1,385 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\BlockListBuilder.
+ */
+
+namespace Drupal\block;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines a class to build a listing of block entities.
+ *
+ * @see \Drupal\block\Entity\Block
+ */
+class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
+
+  /**
+   * The theme containing the blocks.
+   *
+   * @var string
+   */
+  protected $theme;
+
+  /**
+   * The current request.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface
+   */
+  protected $themeManager;
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $limit = FALSE;
+
+  /**
+   * Constructs a new BlockListBuilder object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
+   *   The entity storage class.
+   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
+   *   The theme manager.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder) {
+    parent::__construct($entity_type, $storage);
+
+    $this->themeManager = $theme_manager;
+    $this->formBuilder = $form_builder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('entity.manager')->getStorage($entity_type->id()),
+      $container->get('theme.manager'),
+      $container->get('form_builder')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param string|null $theme
+   *   (optional) The theme to display the blocks for. If NULL, the current
+   *   theme will be used.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   *
+   * @return array
+   *   The block list as a renderable array.
+   */
+  public function render($theme = NULL, Request $request = NULL) {
+    $this->request = $request;
+    $this->theme = $theme;
+
+    return $this->formBuilder->getForm($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'block_admin_display_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['#attached']['library'][] = 'core/drupal.tableheader';
+    $form['#attached']['library'][] = 'block/drupal.block';
+    $form['#attached']['library'][] = 'block/drupal.block.admin';
+    $form['#attributes']['class'][] = 'clearfix';
+
+    // Build the form tree.
+    $form['blocks'] = $this->buildBlocksForm();
+
+    $form['actions'] = array(
+      '#tree' => FALSE,
+      '#type' => 'actions',
+    );
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save blocks'),
+      '#button_type' => 'primary',
+    );
+
+    return $form;
+  }
+
+  /**
+   * Builds the main "Blocks" portion of the form.
+   *
+   * @return array
+   */
+  protected function buildBlocksForm() {
+    // Build blocks first for each region.
+    $blocks = [];
+    $entities = $this->load();
+    /** @var \Drupal\block\BlockInterface[] $entities */
+    foreach ($entities as $entity_id => $entity) {
+      $definition = $entity->getPlugin()->getPluginDefinition();
+      $blocks[$entity->getRegion()][$entity_id] = array(
+        'label' => $entity->label(),
+        'entity_id' => $entity_id,
+        'weight' => $entity->getWeight(),
+        'entity' => $entity,
+        'category' => $definition['category'],
+      );
+    }
+
+    $form = array(
+      '#type' => 'table',
+      '#header' => array(
+        $this->t('Block'),
+        $this->t('Category'),
+        $this->t('Region'),
+        $this->t('Weight'),
+        $this->t('Operations'),
+      ),
+      '#attributes' => array(
+        'id' => 'blocks',
+      ),
+    );
+
+    // Weights range from -delta to +delta, so delta should be at least half
+    // of the amount of blocks present. This makes sure all blocks in the same
+    // region get an unique weight.
+    $weight_delta = round(count($entities) / 2);
+
+    $placement = FALSE;
+    if ($this->request->query->has('block-placement')) {
+      $placement = $this->request->query->get('block-placement');
+      $form['#attached']['drupalSettings']['blockPlacement'] = $placement;
+    }
+
+    // Loop over each region and build blocks.
+    $regions = $this->systemRegionList($this->getThemeName(), REGIONS_VISIBLE);
+    $block_regions_with_disabled = $regions + array(BlockInterface::BLOCK_REGION_NONE => $this->t('Disabled', array(), array('context' => 'Plural')));
+    foreach ($block_regions_with_disabled as $region => $title) {
+      $form['#tabledrag'][] = array(
+        'action' => 'match',
+        'relationship' => 'sibling',
+        'group' => 'block-region-select',
+        'subgroup' => 'block-region-' . $region,
+        'hidden' => FALSE,
+      );
+      $form['#tabledrag'][] = array(
+        'action' => 'order',
+        'relationship' => 'sibling',
+        'group' => 'block-weight',
+        'subgroup' => 'block-weight-' . $region,
+      );
+
+      $form['region-' . $region] = array(
+        '#attributes' => array(
+          'class' => array('region-title', 'region-title-' . $region),
+          'no_striping' => TRUE,
+        ),
+      );
+      $form['region-' . $region]['title'] = array(
+        '#prefix' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : $block_regions_with_disabled[$region],
+        '#type' => 'link',
+        '#title' => $this->t('Place block <span class="visually-hidden">in the %region region</span>', ['%region' => $block_regions_with_disabled[$region]]),
+        '#url' => Url::fromRoute('block.admin_library', ['theme' => $this->getThemeName()], ['query' => ['region' => $region]]),
+        '#wrapper_attributes' => array(
+          'colspan' => 5,
+        ),
+        '#attributes' => [
+          'class' => ['use-ajax', 'button', 'button--small'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 700,
+          ]),
+        ],
+      );
+
+      $form['region-' . $region . '-message'] = array(
+        '#attributes' => array(
+          'class' => array(
+            'region-message',
+            'region-' . $region . '-message',
+            empty($blocks[$region]) ? 'region-empty' : 'region-populated',
+          ),
+        ),
+      );
+      $form['region-' . $region . '-message']['message'] = array(
+        '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
+        '#wrapper_attributes' => array(
+          'colspan' => 5,
+        ),
+      );
+
+      if (isset($blocks[$region])) {
+        foreach ($blocks[$region] as $info) {
+          $entity_id = $info['entity_id'];
+
+          $form[$entity_id] = array(
+            '#attributes' => array(
+              'class' => array('draggable'),
+            ),
+          );
+          if ($placement && $placement == Html::getClass($entity_id)) {
+            $form[$entity_id]['#attributes']['class'][] = 'color-success';
+            $form[$entity_id]['#attributes']['class'][] = 'js-block-placed';
+          }
+          $form[$entity_id]['info'] = array(
+            '#plain_text' => $info['label'],
+            '#wrapper_attributes' => array(
+              'class' => array('block'),
+            ),
+          );
+          $form[$entity_id]['type'] = array(
+            '#markup' => $info['category'],
+          );
+          $form[$entity_id]['region-theme']['region'] = array(
+            '#type' => 'select',
+            '#default_value' => $region,
+            '#empty_value' => BlockInterface::BLOCK_REGION_NONE,
+            '#title' => $this->t('Region for @block block', array('@block' => $info['label'])),
+            '#title_display' => 'invisible',
+            '#options' => $regions,
+            '#attributes' => array(
+              'class' => array('block-region-select', 'block-region-' . $region),
+            ),
+            '#parents' => array('blocks', $entity_id, 'region'),
+          );
+          $form[$entity_id]['region-theme']['theme'] = array(
+            '#type' => 'hidden',
+            '#value' => $this->getThemeName(),
+            '#parents' => array('blocks', $entity_id, 'theme'),
+          );
+          $form[$entity_id]['weight'] = array(
+            '#type' => 'weight',
+            '#default_value' => $info['weight'],
+            '#delta' => $weight_delta,
+            '#title' => $this->t('Weight for @block block', array('@block' => $info['label'])),
+            '#title_display' => 'invisible',
+            '#attributes' => array(
+              'class' => array('block-weight', 'block-weight-' . $region),
+            ),
+          );
+          $form[$entity_id]['operations'] = $this->buildOperations($info['entity']);
+        }
+      }
+    }
+
+    // Do not allow disabling the main system content block when it is present.
+    if (isset($form['system_main']['region'])) {
+      $form['system_main']['region']['#required'] = TRUE;
+    }
+    return $form;
+  }
+
+  /**
+   * Gets the name of the theme used for this block listing.
+   *
+   * @return string
+   *   The name of the theme.
+   */
+  protected function getThemeName() {
+    // If no theme was specified, use the current theme.
+    if (!$this->theme) {
+      $this->theme = $this->themeManager->getActiveTheme()->getName();
+    }
+    return $this->theme;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityIds() {
+    return $this->getStorage()->getQuery()
+      ->condition('theme', $this->getThemeName())
+      ->sort($this->entityType->getKey('id'))
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+
+    if (isset($operations['edit'])) {
+      $operations['edit']['title'] = $this->t('Configure');
+    }
+
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // No validation.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $entities = $this->storage->loadMultiple(array_keys($form_state->getValue('blocks')));
+    /** @var \Drupal\block\BlockInterface[] $entities */
+    foreach ($entities as $entity_id => $entity) {
+      $entity_values = $form_state->getValue(array('blocks', $entity_id));
+      $entity->setWeight($entity_values['weight']);
+      $entity->setRegion($entity_values['region']);
+      if ($entity->getRegion() == BlockInterface::BLOCK_REGION_NONE) {
+        $entity->disable();
+      }
+      else {
+        $entity->enable();
+      }
+      $entity->save();
+    }
+    drupal_set_message(t('The block settings have been updated.'));
+
+    // Remove any previously set block placement.
+    $this->request->query->remove('block-placement');
+  }
+
+  /**
+   * Wraps system_region_list().
+   */
+  protected function systemRegionList($theme, $show = REGIONS_ALL) {
+    return system_region_list($theme, $show);
+  }
+
+}
diff --git a/core/modules/block/src/BlockRepository.php b/core/modules/block/src/BlockRepository.php
index 9d56fde..0b9afc8 100644
--- a/core/modules/block/src/BlockRepository.php
+++ b/core/modules/block/src/BlockRepository.php
@@ -10,6 +10,8 @@
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
+use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
 
 /**
@@ -32,6 +34,20 @@ class BlockRepository implements BlockRepositoryInterface {
   protected $themeManager;
 
   /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
+   */
+  protected $contextRepository;
+
+  /**
+   * The context handler.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected $contextHandler;
+
+  /**
    * Constructs a new BlockRepository.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
@@ -40,11 +56,14 @@ class BlockRepository implements BlockRepositoryInterface {
    *   The theme manager.
    * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
    *   The plugin context handler.
+   * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
+   *   The context repository
    */
-  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler) {
+  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler, ContextRepositoryInterface $context_repository) {
     $this->blockStorage = $entity_manager->getStorage('block');
     $this->themeManager = $theme_manager;
     $this->contextHandler = $context_handler;
+    $this->contextRepository = $context_repository;
   }
 
   /**
@@ -58,6 +77,13 @@ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
     $full = array();
     foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
       /** @var \Drupal\block\BlockInterface $block */
+
+      $block_plugin = $block->getPlugin();
+      if ($block_plugin instanceof ContextAwarePluginInterface) {
+        $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
+        $this->contextHandler->applyContextMapping($block_plugin, $contexts);
+      }
+
       $access = $block->access('view', NULL, TRUE);
       $region = $block->getRegion();
       if (!isset($cacheable_metadata[$region])) {
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index 9a582352..a756c2f 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -104,7 +104,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
       // Allow altering of cacheability metadata or setting #create_placeholder.
       $this->moduleHandler->alter(['block_build', "block_build_" . $plugin->getBaseId()], $build[$entity_id], $plugin);
 
-      if ($plugin instanceof MainContentBlockPluginInterface) {
+      if ($plugin instanceof MainContentBlockPluginInterface || !empty($plugin->getContextMapping())) {
         // Immediately build a #pre_render-able block, since this block cannot
         // be built lazily.
         $build[$entity_id] += static::buildPreRenderableBlock($entity, $this->moduleHandler());
diff --git a/core/modules/block/src/Controller/BlockLibraryController.php b/core/modules/block/src/Controller/BlockLibraryController.php
index 972dd73..57cc8df 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;
@@ -44,6 +45,13 @@ class BlockLibraryController extends ControllerBase {
   protected $localActionManager;
 
   /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\LazyContextRepository
+   */
+  protected $contextRepository;
+
+  /**
    * Constructs a BlockLibraryController object.
    *
    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
@@ -53,10 +61,11 @@ class BlockLibraryController extends ControllerBase {
    * @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, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager, LazyContextRepository $context_repository) {
     $this->blockManager = $block_manager;
     $this->routeMatch = $route_match;
     $this->localActionManager = $local_action_manager;
+    $this->contextRepository = $context_repository;
   }
 
   /**
@@ -66,7 +75,8 @@ public static function create(ContainerInterface $container) {
     return new static(
       $container->get('plugin.manager.block'),
       $container->get('current_route_match'),
-      $container->get('plugin.manager.menu.local_action')
+      $container->get('plugin.manager.menu.local_action'),
+      $container->get('context.repository')
     );
   }
 
@@ -95,7 +105,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/Controller/BlockLibraryController.php.orig b/core/modules/block/src/Controller/BlockLibraryController.php.orig
new file mode 100644
index 0000000..972dd73
--- /dev/null
+++ b/core/modules/block/src/Controller/BlockLibraryController.php.orig
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Controller\BlockLibraryController.
+ */
+
+namespace Drupal\block\Controller;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
+use Drupal\Core\Menu\LocalActionManagerInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a list of block plugins to be added to the layout.
+ */
+class BlockLibraryController extends ControllerBase {
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Core\Block\BlockManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * The route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * The local action manager.
+   *
+   * @var \Drupal\Core\Menu\LocalActionManagerInterface
+   */
+  protected $localActionManager;
+
+  /**
+   * Constructs a BlockLibraryController object.
+   *
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
+   * @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) {
+    $this->blockManager = $block_manager;
+    $this->routeMatch = $route_match;
+    $this->localActionManager = $local_action_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.block'),
+      $container->get('current_route_match'),
+      $container->get('plugin.manager.menu.local_action')
+    );
+  }
+
+  /**
+   * Shows a list of blocks that can be added to a theme's layout.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   * @param string $theme
+   *   Theme key of the block list.
+   *
+   * @return array
+   *   A render array as expected by the renderer.
+   */
+  public function listBlocks(Request $request, $theme) {
+    // Since modals do not render any other part of the page, we need to render
+    // them manually as part of this listing.
+    if ($request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal') {
+      $build['local_actions'] = $this->buildLocalActions();
+    }
+
+    $headers = [
+      ['data' => $this->t('Block')],
+      ['data' => $this->t('Category')],
+      ['data' => $this->t('Operations')],
+    ];
+
+    // Only add blocks which work without any available context.
+    $definitions = $this->blockManager->getDefinitionsForContexts();
+    // Order by category, and then by admin label.
+    $definitions = $this->blockManager->getSortedDefinitions($definitions);
+
+    $region = $request->query->get('region');
+    $rows = [];
+    foreach ($definitions as $plugin_id => $plugin_definition) {
+      $row = [];
+      $row['title']['data'] = [
+        '#markup' => $plugin_definition['admin_label'],
+        '#prefix' => '<div class="block-filter-text-source">',
+        '#suffix' => '</div>',
+      ];
+      $row['category']['data'] = $plugin_definition['category'];
+      $links['add'] = [
+        'title' => $this->t('Place block'),
+        'url' => Url::fromRoute('block.admin_add', ['plugin_id' => $plugin_id, 'theme' => $theme]),
+        'attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 700,
+          ]),
+        ],
+      ];
+      if ($region) {
+        $links['add']['query']['region'] = $region;
+      }
+      $row['operations']['data'] = [
+        '#type' => 'operations',
+        '#links' => $links,
+      ];
+      $rows[] = $row;
+    }
+
+    $build['#attached']['library'][] = 'block/drupal.block.admin';
+
+    $build['filter'] = [
+      '#type' => 'search',
+      '#title' => $this->t('Filter'),
+      '#title_display' => 'invisible',
+      '#size' => 30,
+      '#placeholder' => $this->t('Filter by block name'),
+      '#attributes' => [
+        'class' => ['block-filter-text'],
+        'data-element' => '.block-add-table',
+        'title' => $this->t('Enter a part of the block name to filter by.'),
+      ],
+    ];
+
+    $build['blocks'] = [
+      '#type' => 'table',
+      '#header' => $headers,
+      '#rows' => $rows,
+      '#empty' => $this->t('No blocks available.'),
+      '#attributes' => [
+        'class' => ['block-add-table'],
+      ],
+    ];
+
+    return $build;
+  }
+
+  /**
+   * Builds the local actions for this listing.
+   *
+   * @return array
+   *   An array of local actions for this listing.
+   */
+  protected function buildLocalActions() {
+    $build = $this->localActionManager->getActionsForRoute($this->routeMatch->getRouteName());
+    // Without this workaround, the action links will be rendered as <li> with
+    // no wrapping <ul> element.
+    if (!empty($build)) {
+      $build['#prefix'] = '<ul class="action-links">';
+      $build['#suffix'] = '</ul>';
+    }
+    return $build;
+  }
+
+}
diff --git a/core/modules/block/src/Tests/BlockInterfaceTest.php b/core/modules/block/src/Tests/BlockInterfaceTest.php
index 7215765..01509da 100644
--- a/core/modules/block/src/Tests/BlockInterfaceTest.php
+++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
@@ -98,6 +98,7 @@ public function testBlockInterface() {
           '#options' => $period,
         ),
       ),
+      'context_mapping' => array(),
       'display_message' => array(
         '#type' => 'textfield',
         '#title' => t('Display message'),
diff --git a/core/modules/block/src/Tests/BlockInterfaceTest.php.orig b/core/modules/block/src/Tests/BlockInterfaceTest.php.orig
new file mode 100644
index 0000000..7215765
--- /dev/null
+++ b/core/modules/block/src/Tests/BlockInterfaceTest.php.orig
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Tests\BlockInterfaceTest.
+ */
+
+namespace Drupal\block\Tests;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Form\FormState;
+use Drupal\simpletest\KernelTestBase;
+use Drupal\block\BlockInterface;
+
+/**
+ * Tests that the block plugin can work properly without a supporting entity.
+ *
+ * @group block
+ */
+class BlockInterfaceTest extends KernelTestBase {
+  public static $modules = array('system', 'block', 'block_test', 'user');
+
+  /**
+   * Test configuration and subsequent form() and build() method calls.
+   *
+   * This test is attempting to test the existing block plugin api and all
+   * functionality that is expected to remain consistent. The arrays that are
+   * used for comparison can change, but only to include elements that are
+   * contained within BlockBase or the plugin being tested. Likely these
+   * comparison arrays should get smaller, not larger, as more form/build
+   * elements are moved into a more suitably responsible class.
+   *
+   * Instantiation of the plugin is the primary element being tested here. The
+   * subsequent method calls are just attempting to cause a failure if a
+   * dependency outside of the plugin configuration is required.
+   */
+  public function testBlockInterface() {
+    $manager = $this->container->get('plugin.manager.block');
+    $configuration = array(
+      'label' => 'Custom Display Message',
+    );
+    $expected_configuration = array(
+      'id' => 'test_block_instantiation',
+      'label' => 'Custom Display Message',
+      'provider' => 'block_test',
+      'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
+      'cache' => array(
+        'max_age' => Cache::PERMANENT,
+      ),
+      'display_message' => 'no message set',
+    );
+    // Initial configuration of the block at construction time.
+    /** @var $display_block \Drupal\Core\Block\BlockPluginInterface */
+    $display_block = $manager->createInstance('test_block_instantiation', $configuration);
+    $this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block was configured correctly.');
+
+    // Updating an element of the configuration.
+    $display_block->setConfigurationValue('display_message', 'My custom display message.');
+    $expected_configuration['display_message'] = 'My custom display message.';
+    $this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block configuration was updated correctly.');
+    $definition = $display_block->getPluginDefinition();
+
+    $period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400);
+    $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period));
+    $period[0] = '<' . t('no caching') . '>';
+    $period[\Drupal\Core\Cache\Cache::PERMANENT] = t('Forever');
+    $expected_form = array(
+      'provider' => array(
+        '#type' => 'value',
+        '#value' => 'block_test',
+      ),
+      'admin_label' => array(
+        '#type' => 'item',
+        '#title' => t('Block description'),
+        '#plain_text' => $definition['admin_label'],
+      ),
+      'label' => array(
+        '#type' => 'textfield',
+        '#title' => 'Title',
+        '#maxlength' => 255,
+        '#default_value' => 'Custom Display Message',
+        '#required' => TRUE,
+      ),
+      'label_display' => array(
+        '#type' => 'checkbox',
+        '#title' => 'Display title',
+        '#default_value' => TRUE,
+        '#return_value' => 'visible',
+      ),
+      'cache' => array(
+        '#type' => 'details',
+        '#title' => t('Cache settings'),
+        'max_age' => array(
+          '#type' => 'select',
+          '#title' => t('Maximum age'),
+          '#description' => t('The maximum time this block may be cached.'),
+          '#default_value' => Cache::PERMANENT,
+          '#options' => $period,
+        ),
+      ),
+      'display_message' => array(
+        '#type' => 'textfield',
+        '#title' => t('Display message'),
+        '#default_value' => 'My custom display message.',
+      ),
+    );
+    $form_state = new FormState();
+    // Ensure there are no form elements that do not belong to the plugin.
+    $actual_form = $display_block->buildConfigurationForm(array(), $form_state);
+    // Remove the visibility sections, as that just tests condition plugins.
+    unset($actual_form['visibility'], $actual_form['visibility_tabs']);
+    $this->assertIdentical($actual_form, $expected_form, 'Only the expected form elements were present.');
+
+    $expected_build = array(
+      '#children' => 'My custom display message.',
+    );
+    // Ensure the build array is proper.
+    $this->assertIdentical($display_block->build(), $expected_build, 'The plugin returned the appropriate build array.');
+
+    // Ensure the machine name suggestion is correct. In truth, this is actually
+    // testing BlockBase's implementation, not the interface itself.
+    $this->assertIdentical($display_block->getMachineNameSuggestion(), 'displaymessage', 'The plugin returned the expected machine name suggestion.');
+  }
+}
diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php
index 37fce6e..a26bc71 100644
--- a/core/modules/block/src/Tests/BlockUiTest.php
+++ b/core/modules/block/src/Tests/BlockUiTest.php
@@ -166,20 +166,41 @@ public function testCandidateBlockList() {
    */
   public function testContextAwareBlocks() {
     $arguments = array(
-      ':ul_class' => 'block-list',
-      ':li_class' => 'test-context-aware',
+      ':title' => 'Test context-aware block',
+      ':category' => 'Block test',
       ':href' => 'admin/structure/block/add/test_context_aware/classy',
-      ':text' => 'Test context-aware block',
     );
+    $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.');
   }
 
   /**
+   * Tests the behavior of unsatisfied context-aware blocks.
+   */
+  public function testContextAwareUnsatisfiedBlocks() {
+    $arguments = [
+      ':title' => 'Test context-aware block',
+      ':category' => 'Block test',
+      ':href' => 'admin/structure/block/add/test_context_aware_unsatisfied/classy',
+
+    ];
+    $pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]';
+
+    $this->drupalGet('admin/structure/block');
+    $this->clickLinkPartialName('Place block');
+    $elements = $this->xpath($pattern, $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 exists.');
+  }
+
+  /**
    * Tests that the BlockForm populates machine name correctly.
    */
   public function testMachineNameSuggestion() {
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 0000000..7232e74
--- /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/block/tests/src/Unit/BlockRepositoryTest.php b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
index aab11fe..3fbf0db 100644
--- a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
+++ b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
@@ -10,6 +10,7 @@
 use Drupal\block\BlockRepository;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Plugin\Context\LazyContextRepository;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Tests\UnitTestCase;
 
@@ -40,6 +41,11 @@ class BlockRepositoryTest extends UnitTestCase {
   protected $contextHandler;
 
   /**
+   * @var \Drupal\Core\Plugin\Context\LazyContextRepository|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $contextRepository;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -65,13 +71,14 @@ protected function setUp() {
       ->will($this->returnValue($active_theme));
 
     $this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
+    $this->contextRepository = $this->prophesize(LazyContextRepository::class);
     $this->blockStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
     $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
     $entity_manager->expects($this->any())
       ->method('getStorage')
       ->willReturn($this->blockStorage);
 
-    $this->blockRepository = new BlockRepository($entity_manager, $theme_manager, $this->contextHandler);
+    $this->blockRepository = new BlockRepository($entity_manager, $theme_manager, $this->contextHandler, $this->contextRepository->reveal());
   }
 
   /**
diff --git a/core/modules/node/src/ContextProvider/NodeRouteContext.php b/core/modules/node/src/ContextProvider/NodeRouteContext.php
index e6f5781..6f55b08 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];
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
new file mode 100644
index 0000000..7f8382f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file Contains \Drupal\Tests\Core\Annotation\ContextDefinitionTest.
+ */
+
+namespace Drupal\Tests\Core\Annotation;
+
+use Drupal\Core\Annotation\ContextDefinition;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Annotation\ContextDefinition
+ * @group Annotation
+ */
+class ContextDefinitionTest extends UnitTestCase {
+
+  /**
+   * The translation manager used for testing.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $translationManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->translationManager = $this->getStringTranslationStub();
+  }
+
+  /**
+   * Test the ContextDefinition Annotation.
+   */
+  public function testContextDefinitionAnnotation() {
+    $container = new ContainerBuilder();
+    $container->set('string_translation', $this->translationManager);
+    \Drupal::setContainer($container);
+
+    $values = [
+      'value' => 'entity:node',
+      //'label' => new Translation(['value' => 'test']),
+      'label' => 'test',
+      'description' => 'test',
+    ];
+    $context_definition = new ContextDefinition($values);
+    $definition = $context_definition->get();
+    //throw new \Exception(print_r(, TRUE));
+    // If the label or description are not a Translation annotation instance,
+    // it should be replaced by NULL.
+    $this->assertNull($definition->getLabel());
+    $this->assertNull($definition->getDescription());
+
+    $values = [
+      'value' => 'entity:node',
+      'label' => new Translation(['value' => 'test']),
+      'description' => new Translation(['value' => 'test']),
+    ];
+    $context_definition = new ContextDefinition($values);
+    $definition = $context_definition->get();
+    //throw new \Exception(print_r(, TRUE));
+    // If the label or description are a Translation annotation instance, the
+    // translated string should be returned.
+    $this->assertEquals('test', $definition->getLabel());
+    $this->assertEquals('test', $definition->getDescription());
+  }
+
+}
