diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 9278240..6263d37 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -332,6 +332,9 @@ block_settings:
       sequence:
         type: string
 
+block.settings.*:
+  type: block_settings
+
 condition.plugin:
   type: mapping
   label: 'Condition'
diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index df2a2fd..18742ac 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -78,6 +78,27 @@ core.entity_view_display.*.*.*:
              label: 'Third party settings'
              sequence:
                type: field.formatter.third_party.[%key]
+    blocks:
+      type: sequence
+      label: 'Block fields'
+      sequence:
+        type: mapping
+        mapping:
+          id:
+            type: string
+            label: 'ID'
+          plugin_id:
+            type: string
+            label: 'Plugin ID'
+          weight:
+            type: integer
+            label: 'Weight'
+          region:
+            type: string
+            label: 'Region'
+          settings:
+            type: block.settings.[id]
+
     hidden:
       type: sequence
       label: 'Field display setting'
diff --git a/core/core.services.yml b/core/core.services.yml
index 30fbcc2..b98fae8 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -338,7 +338,7 @@ services:
       - [setValidationConstraintManager, ['@validation.constraint']]
   context.handler:
     class: Drupal\Core\Plugin\Context\ContextHandler
-    arguments: ['@typed_data_manager']
+    arguments: ['@context.repository']
   context.repository:
     class: Drupal\Core\Plugin\Context\LazyContextRepository
     arguments: ['@service_container']
diff --git a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
index 3598b51..a930f30 100644
--- a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
+++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php
@@ -46,4 +46,24 @@ public function build(FieldableEntityInterface $entity);
    */
   public function buildMultiple(array $entities);
 
+  /**
+   * @todo.
+   */
+  public function getBlocks();
+
+  /**
+   * @todo.
+   */
+  public function getBlock($name);
+
+  /**
+   * @todo.
+   */
+  public function removeBlock($name);
+
+  /**
+   * @todo.
+   */
+  public function setBlock($name, $block);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 74b15b6..0a29fb1 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\EntityDisplayBase;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 
 /**
@@ -27,6 +28,7 @@
  *     "bundle",
  *     "mode",
  *     "content",
+ *     "blocks",
  *     "hidden",
  *   }
  * )
@@ -39,6 +41,47 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
   protected $displayContext = 'view';
 
   /**
+   * @todo.
+   *
+   * @var array
+   */
+  protected $blocks = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBlocks() {
+    return $this->blocks;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBlock($name) {
+    $blocks = $this->getBlocks();
+    return isset($blocks[$name]) ? $blocks[$name] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeBlock($name) {
+    unset($this->blocks[$name]);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setBlock($name, $block) {
+    $this->blocks[$name] = $block + [
+      'weight' => 0,
+      'region' => $this->getDefaultRegion(),
+    ];
+    return $this;
+  }
+
+  /**
    * Returns the display objects used to render a set of entities.
    *
    * Depending on the configuration of the view mode for each bundle, this can
@@ -263,6 +306,21 @@ public function buildMultiple(array $entities) {
       }
     }
 
+    foreach ($this->getBlocks() as $name => $block_info) {
+      /** @var \Drupal\Core\Block\BlockPluginInterface $block */
+      $block = \Drupal::service('plugin.manager.block')->createInstance($block_info['plugin_id'], $block_info['settings']);
+
+      if ($block instanceof ContextAwarePluginInterface) {
+        \Drupal::service('context.handler')->applyRuntimeContext($block);
+      }
+
+      $block_access = $block->access(\Drupal::currentUser(), TRUE);
+      foreach ($entities as $id => $entity) {
+        $build_list[$id][$name] = $block_access->isAllowed() ? $block->build() : [];
+        $this->renderer->addCacheableDependency($build_list[$id][$name], $block_access);
+      }
+    }
+
     foreach ($entities as $id => $entity) {
       // Assign the configured weights.
       foreach ($this->getComponents() as $name => $options) {
diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
index 2a13d36..470511d 100644
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
@@ -12,6 +12,23 @@
 class ContextHandler implements ContextHandlerInterface {
 
   /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
+   */
+  protected $contextRepository;
+
+  /**
+   * Constructs a new ContextHandler.
+   *
+   * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
+   *   The context repository.
+   */
+  public function __construct(ContextRepositoryInterface $context_repository) {
+    $this->contextRepository = $context_repository;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
@@ -118,4 +135,12 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function applyRuntimeContext(ContextAwarePluginInterface $plugin) {
+    $contexts = $this->contextRepository->getRuntimeContexts(array_values($plugin->getContextMapping()));
+    $this->applyContextMapping($plugin, $contexts);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php
index a0d5703..1c5c622 100644
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php
@@ -61,6 +61,9 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
   /**
    * Prepares a plugin for evaluation.
    *
+   * This method is for more complex use cases, see ::applyRuntimeContext() for
+   * the more common approach.
+   *
    * @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin
    *   A plugin about to be evaluated.
    * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
@@ -77,4 +80,12 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
    */
   public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = []);
 
+  /**
+   * Applies all relevant runtime contexts to a plugin.
+   *
+   * @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin
+   *   A context-aware plugin.
+   */
+  public function applyRuntimeContext(ContextAwarePluginInterface $plugin);
+
 }
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index 16d79bc..7dc4f53 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -30,6 +30,3 @@ block.block.*:
       sequence:
         type: condition.plugin.[id]
         label: 'Visibility Condition'
-
-block.settings.*:
-  type: block_settings
diff --git a/core/modules/block/src/BlockAccessControlHandler.php b/core/modules/block/src/BlockAccessControlHandler.php
index 176c326..73af3c4 100644
--- a/core/modules/block/src/BlockAccessControlHandler.php
+++ b/core/modules/block/src/BlockAccessControlHandler.php
@@ -12,7 +12,6 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
-use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -34,20 +33,12 @@ class BlockAccessControlHandler extends EntityAccessControlHandler implements En
   protected $contextHandler;
 
   /**
-   * The context manager service.
-   *
-   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
-   */
-  protected $contextRepository;
-
-  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
     return new static(
       $entity_type,
-      $container->get('context.handler'),
-      $container->get('context.repository')
+      $container->get('context.handler')
     );
   }
 
@@ -58,13 +49,10 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    *   The entity type definition.
    * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
    *   The ContextHandler for applying contexts to conditions properly.
-   * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
-   *   The lazy context repository service.
    */
-  public function __construct(EntityTypeInterface $entity_type, ContextHandlerInterface $context_handler, ContextRepositoryInterface $context_repository ) {
+  public function __construct(EntityTypeInterface $entity_type, ContextHandlerInterface $context_handler) {
     parent::__construct($entity_type);
     $this->contextHandler = $context_handler;
-    $this->contextRepository = $context_repository;
   }
 
 
@@ -87,8 +75,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
         if ($condition instanceof ContextAwarePluginInterface) {
           try {
-            $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
-            $this->contextHandler->applyContextMapping($condition, $contexts);
+            $this->contextHandler->applyRuntimeContext($condition);
           }
           catch (ContextException $e) {
             $missing_context = TRUE;
@@ -113,8 +100,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
         $block_plugin = $entity->getPlugin();
         try {
           if ($block_plugin instanceof ContextAwarePluginInterface) {
-            $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
-            $this->contextHandler->applyContextMapping($block_plugin, $contexts);
+            $this->contextHandler->applyRuntimeContext($block_plugin);
           }
           $access = $block_plugin->access($account, TRUE);
         }
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index 3b20d7b..018d617 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -140,8 +140,7 @@ protected static function buildPreRenderableBlock($entity, ModuleHandlerInterfac
 
     // Inject runtime contexts.
     if ($plugin instanceof ContextAwarePluginInterface) {
-      $contexts = \Drupal::service('context.repository')->getRuntimeContexts($plugin->getContextMapping());
-      \Drupal::service('context.handler')->applyContextMapping($plugin, $contexts);
+      \Drupal::service('context.handler')->applyRuntimeContext($plugin);
     }
 
     // Create the render array for the block as a whole.
diff --git a/core/modules/field_layout/src/Form/FieldLayoutEntityViewDisplayEditForm.php b/core/modules/field_layout/src/Form/FieldLayoutEntityViewDisplayEditForm.php
index 92bfbde..11725d8 100644
--- a/core/modules/field_layout/src/Form/FieldLayoutEntityViewDisplayEditForm.php
+++ b/core/modules/field_layout/src/Form/FieldLayoutEntityViewDisplayEditForm.php
@@ -3,6 +3,7 @@
 namespace Drupal\field_layout\Form;
 
 use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Core\Block\BlockManagerInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Layout\LayoutPluginManagerInterface;
 use Drupal\field_ui\Form\EntityViewDisplayEditForm;
@@ -22,11 +23,13 @@ class FieldLayoutEntityViewDisplayEditForm extends EntityViewDisplayEditForm {
    *   The field type manager.
    * @param \Drupal\Component\Plugin\PluginManagerBase $plugin_manager
    *   The formatter plugin manager.
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
    * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_plugin_manager
    *   The field layout plugin manager.
    */
-  public function __construct(FieldTypePluginManagerInterface $field_type_manager, PluginManagerBase $plugin_manager, LayoutPluginManagerInterface $layout_plugin_manager) {
-    parent::__construct($field_type_manager, $plugin_manager);
+  public function __construct(FieldTypePluginManagerInterface $field_type_manager, PluginManagerBase $plugin_manager, BlockManagerInterface $block_manager, LayoutPluginManagerInterface $layout_plugin_manager) {
+    parent::__construct($field_type_manager, $plugin_manager, $block_manager);
     $this->layoutPluginManager = $layout_plugin_manager;
   }
 
@@ -37,6 +40,7 @@ public static function create(ContainerInterface $container) {
     return new static(
       $container->get('plugin.manager.field.field_type'),
       $container->get('plugin.manager.field.formatter'),
+      $container->get('plugin.manager.block'),
       $container->get('plugin.manager.core.layout')
     );
   }
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 79b3f15..8f54bdf 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityViewModeInterface;
 use Drupal\Core\Entity\EntityFormModeInterface;
 use Drupal\Core\Url;
+use Drupal\field_ui\Controller\FieldAddBlockController;
 use Drupal\field_ui\FieldUI;
 use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask;
 
@@ -81,6 +82,7 @@ function field_ui_entity_type_build(array &$entity_types) {
 
   $entity_types['entity_form_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityFormDisplayEditForm');
   $entity_types['entity_view_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityViewDisplayEditForm');
+  $entity_types['entity_view_display']->setFormClass('add_block', FieldAddBlockController::class);
 
   $form_mode = $entity_types['entity_form_mode'];
   $form_mode->setListBuilderClass('Drupal\field_ui\EntityFormModeListBuilder');
diff --git a/core/modules/field_ui/src/Controller/FieldAddBlockController.php b/core/modules/field_ui/src/Controller/FieldAddBlockController.php
new file mode 100644
index 0000000..273becc
--- /dev/null
+++ b/core/modules/field_ui/src/Controller/FieldAddBlockController.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace Drupal\field_ui\Controller;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\field_ui\FieldUI;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @todo.
+ */
+class FieldAddBlockController extends EntityForm {
+
+  /**
+   * The entity being used by this form.
+   *
+   * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+   */
+  protected $entity;
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Core\Block\BlockManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\LazyContextRepository
+   */
+  protected $contextRepository;
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * Constructs a BlockLibraryController object.
+   *
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
+   * @param \Drupal\Core\Plugin\Context\ContextRepositoryInterface $context_repository
+   *   The context repository.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   The entity field manager.
+   */
+  public function __construct(BlockManagerInterface $block_manager, ContextRepositoryInterface $context_repository, EntityFieldManagerInterface $entity_field_manager) {
+    $this->blockManager = $block_manager;
+    $this->contextRepository = $context_repository;
+    $this->entityFieldManager = $entity_field_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.block'),
+      $container->get('context.repository'),
+      $container->get('entity_field.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
+    $route_parameters = $route_match->getParameters()->all();
+
+    return $this->getEntityDisplay($route_parameters['entity_type_id'], $route_parameters['bundle'], $route_parameters['view_mode_name']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityDisplay($entity_type_id, $bundle, $mode) {
+    return entity_get_display($entity_type_id, $bundle, $mode);
+  }
+
+  /**
+   * @todo.
+   *
+   * @return array
+   *   A render array as expected by the renderer.
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form['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.'),
+      ],
+    ];
+    $form['blocks'] = [
+      '#type' => 'tableselect',
+      '#header' => [
+        'title' => $this->t('Block'),
+        'category' => $this->t('Category'),
+      ],
+      '#js_select' => FALSE,
+      '#empty' => $this->t('No blocks available.'),
+      '#attributes' => [
+        'class' => ['block-add-table'],
+      ],
+      '#element_validate' => [[$this, 'validateTableselect']],
+    ];
+
+    // Only add blocks which work without any available context.
+    $definitions = $this->blockManager->getDefinitionsForContexts($this->contextRepository->getAvailableContexts());
+    // Order by category, and then by admin label.
+    $definitions = $this->blockManager->getSortedDefinitions($definitions);
+
+    foreach ($definitions as $plugin_id => $plugin_definition) {
+      $form['blocks']['#options'][$plugin_id] = [
+        'title' => [
+          'data' => [
+            '#type' => 'inline_template',
+            '#template' => '<div class="block-filter-text-source">{{ label }}</div>',
+            '#context' => [
+              'label' => $plugin_definition['admin_label'],
+            ],
+          ],
+        ],
+        'category' => $plugin_definition['category'],
+      ];
+    }
+
+    $form['#attached']['library'][] = 'block/drupal.block.admin';
+
+
+    return $form;
+  }
+
+  /**
+   * @todo.
+   */
+  public function validateTableselect($element, FormStateInterface $form_state) {
+    $values = array_filter(NestedArray::getValue($form_state->getValues(), $element['#parents']));
+    $form_state->setValueForElement($element, $values);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
+    $block_ids = [];
+    foreach (array_keys(array_filter($form_state->getValue('blocks'))) as $block_id) {
+      /** @var \Drupal\Core\Block\BlockPluginInterface $block */
+      $block = $this->blockManager->createInstance($block_id);
+      $suggestion = $block->getMachineNameSuggestion();
+      $count = 1;
+      $machine_default = $suggestion;
+      while (isset($block_ids[$machine_default])) {
+        $machine_default = $suggestion . '_' . ++$count;
+      }
+      $block->setConfigurationValue('label', $block->label());
+      $entity->setBlock($machine_default, [
+        'id' => $machine_default,
+        'plugin_id' => $block_id,
+        'settings' => $block->getConfiguration(),
+      ]);
+      $form_state->unsetValue(['blocks', $block_id]);
+    }
+    $this->entityFieldManager->clearCachedFieldDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm($form, $form_state);
+    $entity_type = $this->entityTypeManager->getDefinition($this->entity->getTargetEntityTypeId());
+    $form_state->setRedirect('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.view_mode', [
+      'view_mode_name' => $this->entity->getMode(),
+    ] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()));
+    drupal_set_message($this->t('Your settings have been saved.'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(array $form, FormStateInterface $form_state) {
+    $actions = parent::actions($form, $form_state);
+    $actions['submit']['#value'] = $this->t('Add block');
+    return $actions;
+  }
+
+}
diff --git a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
index c27f3d4..303da96 100644
--- a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
+++ b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
@@ -2,9 +2,16 @@
 
 namespace Drupal\field_ui\Form;
 
+use Drupal\Component\Plugin\ContextAwarePluginInterface;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Field\PluginSettingsInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\Core\Url;
 use Drupal\field_ui\FieldUI;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -15,17 +22,47 @@
 class EntityViewDisplayEditForm extends EntityDisplayFormBase {
 
   /**
+   * The entity being used by this form.
+   *
+   * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+   */
+  protected $entity;
+
+  /**
    * {@inheritdoc}
    */
   protected $displayContext = 'view';
 
   /**
+   * The block manager.
+   *
+   * @var \Drupal\Core\Block\BlockManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * EntityViewDisplayEditForm constructor.
+   *
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type manager.
+   * @param \Drupal\Component\Plugin\PluginManagerBase $plugin_manager
+   *   The formatter plugin manager.
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
+   */
+  public function __construct(FieldTypePluginManagerInterface $field_type_manager, PluginManagerBase $plugin_manager, BlockManagerInterface $block_manager) {
+    parent::__construct($field_type_manager, $plugin_manager);
+    $this->blockManager = $block_manager;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('plugin.manager.field.field_type'),
-      $container->get('plugin.manager.field.formatter')
+      $container->get('plugin.manager.field.formatter'),
+      $container->get('plugin.manager.block')
     );
   }
 
@@ -83,6 +120,203 @@ protected function buildExtraFieldRow($field_id, $extra_field) {
   /**
    * {@inheritdoc}
    */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+
+    $form_state->setTemporaryValue('gathered_contexts', \Drupal::service('context.repository')->getAvailableContexts());
+    foreach ($this->entity->getBlocks() as $block_id => $block_info) {
+      $form['fields'][$block_id] = $this->buildBlockFieldRow($block_id, $block_info, $form, $form_state);
+    }
+
+    $form['add_block'] = [
+      '#weight' => -100,
+      '#type' => 'link',
+      '#title' => $this->t('Add field block'),
+      '#url' => Url::fromRoute('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.add_block', $this->getRouteBundleParameters($this->entity->getMode())),
+      '#attributes' => [
+        'class' => ['use-ajax', 'button', 'button--small'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ],
+    ];
+    return $form;
+  }
+
+  /**
+   * @todo.
+   */
+  protected function getRouteBundleParameters($mode) {
+    $entity_type = $this->entityTypeManager->getDefinition($this->entity->getTargetEntityTypeId());
+    return ['view_mode_name' => $mode] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle());
+  }
+
+  /**
+   * @todo.
+   */
+  protected function buildBlockFieldRow($field_id, $extra_field, $form, FormStateInterface $form_state) {
+    $display_options = $this->entity->getBlock($field_id);
+    /** @var \Drupal\Core\Block\BlockPluginInterface $block */
+    $block = $this->blockManager->createInstance($extra_field['plugin_id'], $extra_field['settings']);
+
+    $regions = array_keys($this->getRegions());
+    $label = $block->label();
+    $extra_field_row = [
+      '#attributes' => ['class' => ['draggable', 'tabledrag-leaf']],
+      '#row_type' => 'extra_field',
+      '#region_callback' => [$this, 'getRowRegion'],
+      '#js_settings' => ['rowHandler' => 'field'],
+      'human_name' => [
+        '#markup' => $label,
+      ],
+      'weight' => [
+        '#type' => 'textfield',
+        '#title' => $this->t('Weight for @title', ['@title' => $label]),
+        '#title_display' => 'invisible',
+        '#default_value' => $display_options ? $display_options['weight'] : 0,
+        '#size' => 3,
+        '#attributes' => ['class' => ['field-weight']],
+      ],
+      'parent_wrapper' => [
+        'parent' => [
+          '#type' => 'select',
+          '#title' => $this->t('Parents for @title', ['@title' => $label]),
+          '#title_display' => 'invisible',
+          '#options' => array_combine($regions, $regions),
+          '#empty_value' => '',
+          '#attributes' => ['class' => ['js-field-parent', 'field-parent']],
+          '#parents' => ['fields', $field_id, 'parent'],
+        ],
+        'hidden_name' => [
+          '#type' => 'hidden',
+          '#default_value' => $field_id,
+          '#attributes' => ['class' => ['field-name']],
+        ],
+      ],
+      'region' => [
+        '#type' => 'select',
+        '#title' => $this->t('Region for @title', ['@title' => $label]),
+        '#title_display' => 'invisible',
+        '#options' => $this->getRegionOptions(),
+        '#default_value' => $display_options ? $display_options['region'] : 'visible',
+        '#attributes' => ['class' => ['field-region']],
+      ],
+      'empty_cell' => [
+        '#markup' => '&nbsp;',
+      ],
+      'plugin' => [
+        'type' => [
+          '#type' => 'hidden',
+          '#value' => $display_options ? 'visible' : 'hidden',
+          '#parents' => ['fields', $field_id, 'type'],
+          '#attributes' => ['class' => ['field-plugin-type']],
+        ],
+      ],
+      'settings_summary' => [],
+      'settings_edit' => [],
+    ];
+
+    // Base button element for the various plugin settings actions.
+    $base_button = [
+      '#submit' => ['::multistepSubmit'],
+      '#ajax' => [
+        'callback' => '::multistepAjax',
+        'wrapper' => 'field-display-overview-wrapper',
+        'effect' => 'fade',
+      ],
+      '#field_name' => $field_id,
+    ];
+
+    if ($form_state->get('plugin_settings_edit') == $field_id) {
+      // Generate the settings form and allow other modules to alter it.
+      $extra_field_row['plugin']['#cell_attributes'] = ['colspan' => 3];
+      $extra_field_row['plugin']['settings_edit_form'] = [
+        '#type' => 'container',
+        '#attributes' => ['class' => ['field-plugin-settings-edit-form']],
+        '#parents' => ['fields', $field_id, 'settings_edit_form'],
+        'label' => [
+          '#markup' => $this->t('Plugin settings'),
+        ],
+        'settings' => [],
+        'actions' => [
+          '#type' => 'actions',
+          'save_settings' => $base_button + [
+            '#type' => 'submit',
+            '#button_type' => 'primary',
+            '#name' => $field_id . '_plugin_settings_update',
+            '#value' => $this->t('Update'),
+            '#op' => 'update',
+          ],
+          'cancel_settings' => $base_button + [
+            '#type' => 'submit',
+            '#name' => $field_id . '_plugin_settings_cancel',
+            '#value' => $this->t('Cancel'),
+            '#op' => 'cancel',
+            // Do not check errors for the 'Cancel' button, but make sure we
+            // get the value of the 'plugin type' select.
+            '#limit_validation_errors' => [['fields', $field_id, 'type']],
+          ],
+        ],
+      ];
+      $extra_field_row['#attributes']['class'][] = 'field-plugin-settings-editing';
+      $subform_state = SubformState::createForSubform($extra_field_row['plugin']['settings_edit_form']['settings'], $form, $form_state);
+      $extra_field_row['plugin']['settings_edit_form']['settings'] = $block->buildConfigurationForm($extra_field_row['plugin']['settings_edit_form']['settings'], $subform_state);
+    }
+    else {
+      // Check selected plugin settings to display edit link or not.
+      $extra_field_row['settings_edit'] = $base_button + [
+        '#type' => 'image_button',
+        '#name' => $field_id . '_settings_edit',
+        '#src' => 'core/misc/icons/787878/cog.svg',
+        '#attributes' => ['class' => ['field-plugin-settings-edit'], 'alt' => $this->t('Edit')],
+        '#op' => 'edit',
+        // Do not check errors for the 'Edit' button, but make sure we get
+        // the value of the 'plugin type' select.
+        '#limit_validation_errors' => [['fields', $field_id, 'type']],
+        '#prefix' => '<div class="field-plugin-settings-edit-wrapper">',
+        '#suffix' => '</div>',
+      ];
+    }
+
+    return $extra_field_row;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $entity */
+    parent::copyFormValuesToEntity($entity, $form, $form_state);
+
+    foreach ($entity->getBlocks() as $name => $block_info) {
+      $form_values = $form_state->getValue(['fields', $name]);
+      $options = $entity->getBlock($name);
+      if (!isset($options['id']) || $form_values['region'] == 'hidden') {
+        $entity->removeBlock($name);
+      }
+      else {
+        if ($form_state->get('plugin_settings_update') === $name) {
+          $form_state->set('plugin_settings_update', NULL);
+          $block = $this->blockManager->createInstance($options['plugin_id'], $options['settings']);
+          $sub_form_state = SubformState::createForSubform($form['fields'][$name]['plugin']['settings_edit_form']['settings'], $form, $form_state);
+          $block->submitConfigurationForm($form, $sub_form_state);
+          if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
+            $context_mapping = $sub_form_state->getValue('context_mapping', []);
+            $block->setContextMapping($context_mapping);
+          }
+          $options['settings'] = $block->getConfiguration();
+        }
+        $options['weight'] = $form_values['weight'];
+        $options['region'] = $form_values['region'];
+        $entity->setBlock($name, $options);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   protected function getEntityDisplay($entity_type_id, $bundle, $mode) {
     return entity_get_display($entity_type_id, $bundle, $mode);
   }
@@ -137,10 +371,7 @@ protected function getTableHeader() {
    * {@inheritdoc}
    */
   protected function getOverviewUrl($mode) {
-    $entity_type = $this->entityManager->getDefinition($this->entity->getTargetEntityTypeId());
-    return Url::fromRoute('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.view_mode', [
-      'view_mode_name' => $mode,
-    ] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()));
+    return Url::fromRoute('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.view_mode', $this->getRouteBundleParameters($mode));
   }
 
   /**
diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php
index b08e7f8..0f9a66b 100644
--- a/core/modules/field_ui/src/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php
@@ -154,6 +154,17 @@ protected function alterRoutes(RouteCollection $collection) {
           $options
         );
         $collection->add("entity.entity_view_display.{$entity_type_id}.view_mode", $route);
+
+        $route = new Route(
+          "$path/display/{view_mode_name}/add-block",
+          [
+            '_entity_form' => 'entity_view_display.add_block',
+            '_title' => 'Add block',
+          ] + $defaults,
+          ['_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'],
+          $options
+        );
+        $collection->add("entity.entity_view_display.{$entity_type_id}.add_block", $route);
       }
     }
   }
diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php
index 6da3c41..b6a092b 100644
--- a/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php
+++ b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php
@@ -15,7 +15,12 @@ class EntityDisplayTest extends JavascriptTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['field_ui', 'entity_test'];
+  public static $modules = ['field_ui', 'entity_test', 'block_test'];
+
+  /**
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $user;
 
   /**
    * {@inheritdoc}
@@ -30,7 +35,7 @@ protected function setUp() {
       ]],
     ]);
     $entity->save();
-    $this->drupalLogin($this->drupalCreateUser([
+    $this->user = $this->drupalCreateUser([
       'access administration pages',
       'view test entity',
       'administer entity_test content',
@@ -38,7 +43,8 @@ protected function setUp() {
       'administer entity_test display',
       'administer entity_test form display',
       'view the administration theme',
-    ]));
+    ]);
+    $this->drupalLogin($this->user);
   }
 
   /**
@@ -87,6 +93,41 @@ public function testEntityView() {
   }
 
   /**
+   * Tests that block-based extra fields are available.
+   */
+  public function testEntityViewWithBlocks() {
+    $this->drupalGet('entity_test/structure/entity_test/display');
+    $this->clickLink('Add field block');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    // Ensure that blocks with unsatisfiable contexts are not shown.
+    $this->assertSession()->pageTextNotContains('Test context-aware unsatisfied block');
+    // Ensure that context-aware blocks are shown.
+    $this->assertSession()->pageTextContains('Test context-aware block');
+
+    // Set the context-aware to visible, but do not assign a context mapping.
+    $this->getSession()->getPage()->checkField('blocks[test_context_aware]');
+    $this->getSession()->getPage()->find('css', '.ui-dialog-buttonpane .button--primary')->press();
+    $this->assertSession()->pageTextContains('Your settings have been saved.');
+
+    $this->drupalGet('entity_test/1');
+    $this->assertSession()->pageTextContains('No context mapping selected.');
+
+    // Set the context mapping.
+    $this->drupalGet('entity_test/structure/entity_test/display');
+    $this->click('#edit-fields-testcontextawareblock-settings-edit');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->getSession()->getPage()->selectFieldOption('fields[testcontextawareblock][settings_edit_form][settings][context_mapping][user]', '@user.current_user_context:current_user');
+    $this->submitForm([], 'Update');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->submitForm([], 'Save');
+    $this->assertSession()->pageTextContains('Your settings have been saved.');
+
+    $this->drupalGet('entity_test/1');
+    $this->assertSession()->pageTextNotContains('No context mapping selected');
+    $this->assertSession()->pageTextContains($this->user->getAccountName());
+  }
+
+  /**
    * Tests extra fields.
    */
   public function testExtraFields() {
diff --git a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
index 4ed5e6e..989af05 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
@@ -11,6 +11,8 @@
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\Plugin\Context\ContextHandler;
+use Drupal\Core\Plugin\Context\ContextInterface;
+use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\Plugin\DataType\StringData;
@@ -30,12 +32,20 @@ class ContextHandlerTest extends UnitTestCase {
   protected $contextHandler;
 
   /**
+   * The context repository.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
+   */
+  protected $contextRepository;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
-    $this->contextHandler = new ContextHandler();
+    $this->contextRepository = $this->prophesize(ContextRepositoryInterface::class);
+    $this->contextHandler = new ContextHandler($this->contextRepository->reveal());
   }
 
   /**
@@ -487,6 +497,37 @@ public function testApplyContextMappingConfigurableAssignedMiss() {
     $this->contextHandler->applyContextMapping($plugin, $contexts, ['miss' => 'name']);
   }
 
+  /**
+   * @covers ::applyRuntimeContext
+   * @covers ::applyContextMapping
+   */
+  public function testApplyRuntimeContext() {
+    $context_data = StringData::createInstance(DataDefinition::create('string'));
+    $context_data->setValue('foo');
+    $context = $this->prophesize(ContextInterface::class);
+    $context->getContextData()->willReturn($context_data);
+    $context->hasContextValue()->willReturn(TRUE);
+
+    $contexts = [
+      'name' => $context->reveal(),
+    ];
+
+    $context_definition = (new ContextDefinition())->setRequired(FALSE);
+
+    $plugin_context = $this->prophesize(ContextInterface::class);
+    $plugin_context->addCacheableDependency($context)->shouldBeCalled();
+
+    $plugin = $this->prophesize(ContextAwarePluginInterface::class);
+    $plugin->getContextMapping()->willReturn(['hit' => 'name']);
+    $plugin->getContextDefinitions()->willReturn(['hit' => $context_definition]);
+    $plugin->getContext('hit')->willReturn($plugin_context->reveal());
+    $plugin->setContextValue('hit', $context_data)->shouldBeCalled();
+
+    $this->contextRepository->getRuntimeContexts(['name'])->willReturn($contexts)->shouldBeCalled();
+
+    $this->contextHandler->applyRuntimeContext($plugin->reveal());
+  }
+
 }
 
 interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface {
