From 633d84281a84a518a9b6f09b4cab6cd4ceed670e Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 19 Nov 2014 21:03:06 +0000
Subject: [PATCH] =?UTF-8?q?Issue=20#2339151=20by=20EclipseGc,=20tim.plunke?=
 =?UTF-8?q?tt,=20G=C3=A1bor=20Hojtsy,=20effulgentsia:=20Conditions=20/=20c?=
 =?UTF-8?q?ontext=20system=20does=20not=20allow=20for=20multiple=20configu?=
 =?UTF-8?q?rable=20contexts,=20eg.=20language=20types?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 core/config/schema/core.data_types.schema.yml |   6 -
 .../Plugin/ContextAwarePluginInterface.php    |  24 +++
 .../Core/Annotation/ContextDefinition.php     |  14 +-
 core/lib/Drupal/Core/Block/BlockBase.php      | 190 +-----------------
 .../Core/Block/BlockPluginInterface.php       |  32 ---
 .../Core/Condition/ConditionPluginBase.php    |   6 +
 .../Condition/ConditionPluginCollection.php   |   6 +
 core/lib/Drupal/Core/Entity/EntityForm.php    |   9 +-
 .../Core/Plugin/Context/ContextHandler.php    |   8 +-
 .../ContextAwarePluginAssignmentTrait.php     |  72 +++++++
 .../Core/Plugin/ContextAwarePluginBase.php    |  42 ++++
 core/modules/block/block.module               |   2 +-
 core/modules/block/block.services.yml         |   2 +-
 .../block/config/schema/block.schema.yml      |   6 +
 core/modules/block/js/block.js                |   6 +-
 .../block/src/BlockAccessControlHandler.php   |  85 +++++++-
 core/modules/block/src/BlockForm.php          | 163 ++++++++++++++-
 core/modules/block/src/BlockInterface.php     |  49 +++++
 core/modules/block/src/BlockRepository.php    |  12 +-
 .../block/src/BlockRepositoryInterface.php    |   5 +-
 core/modules/block/src/Entity/Block.php       |  97 ++++++++-
 .../src/Event/BlockConditionContextEvent.php  |  39 ----
 .../block/src/Event/BlockContextEvent.php     |  53 +++++
 core/modules/block/src/Event/BlockEvents.php  |  12 +-
 .../BlockConditionContextSubscriberBase.php   |  61 ------
 .../BlockContextSubscriberBase.php            |  75 +++++++
 .../CurrentLanguageContext.php                |  25 ++-
 .../EventSubscriber/CurrentUserContext.php    |  14 +-
 .../src/EventSubscriber/NodeRouteContext.php  |  17 +-
 .../DisplayVariant/BlockPageVariant.php       |  24 ++-
 .../block/src/Tests/BlockInterfaceTest.php    |   1 -
 .../block/src/Tests/BlockLanguageTest.php     |  78 ++++++-
 .../block/src/Tests/BlockStorageUnitTest.php  |   4 +-
 core/modules/block/src/Tests/BlockTest.php    |  44 +++-
 .../config/install/block.block.test_block.yml |   2 +-
 .../block/tests/src/Unit/BlockFormTest.php    |  68 +++++--
 .../tests/src/Unit/BlockRepositoryTest.php    | 110 ++++++++--
 .../DisplayVariant/BlockPageVariantTest.php   |  21 +-
 .../install/block.block.foobargorilla.yml     |  10 +-
 .../image/src/Form/ImageStyleEditForm.php     |  13 --
 .../src/Plugin/Condition/Language.php         |   8 +-
 .../LanguageBlockSettingsVisibilityTest.php   |   8 +-
 .../src/Tests/d6/MigrateBlockTest.php         |  24 +--
 .../src/Tests/NodeBlockFunctionalTest.php     |   3 +
 .../search/src/Tests/SearchBlockTest.php      |   2 +-
 core/modules/simpletest/src/WebTestBase.php   |   7 +-
 .../src/Unit/Plugin/Block/ViewsBlockTest.php  |   8 -
 .../install/block.block.stark_admin.yml       |   2 +-
 .../install/block.block.stark_login.yml       |   2 +-
 .../install/block.block.stark_tools.yml       |   2 +-
 .../block.block.bartik_account_menu.yml       |   2 +-
 .../block.block.bartik_breadcrumbs.yml        |   2 +-
 .../install/block.block.bartik_content.yml    |   2 +-
 .../install/block.block.bartik_footer.yml     |   2 +-
 .../install/block.block.bartik_help.yml       |   2 +-
 .../install/block.block.bartik_login.yml      |   2 +-
 .../install/block.block.bartik_main_menu.yml  |   2 +-
 .../install/block.block.bartik_powered.yml    |   2 +-
 .../install/block.block.bartik_search.yml     |   2 +-
 .../install/block.block.bartik_tools.yml      |   2 +-
 .../install/block.block.seven_breadcrumbs.yml |   2 +-
 .../install/block.block.seven_content.yml     |   2 +-
 .../config/install/block.block.seven_help.yml |   2 +-
 .../install/block.block.seven_login.yml       |   2 +-
 .../Drupal/Tests/Core/Block/BlockBaseTest.php |  57 ------
 .../Tests/Core/Entity/EntityFormTest.php      |  29 +++
 .../Tests/Core/Plugin/ContextHandlerTest.php  |  12 ++
 67 files changed, 1151 insertions(+), 548 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
 delete mode 100644 core/modules/block/src/Event/BlockConditionContextEvent.php
 create mode 100644 core/modules/block/src/Event/BlockContextEvent.php
 delete mode 100644 core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php
 create mode 100644 core/modules/block/src/EventSubscriber/BlockContextSubscriberBase.php

diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index ffd998869db9..4f6ef0cefc9b 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -300,12 +300,6 @@ block_settings:
     view_mode:
       type: string
       label: 'View mode'
-    visibility:
-      type: sequence
-      label: 'Visibility Conditions'
-      sequence:
-        - type: condition.plugin.[id]
-          label: 'Visibility Condition'
     provider:
       type: string
       label: 'Provider'
diff --git a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php
index e88cefa01470..2ba0a7ee4daf 100644
--- a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php
+++ b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php
@@ -125,4 +125,28 @@ public function setContextValue($name, $value);
    */
   public function validateContexts();
 
+  /**
+   * Returns a mapping of the expected assignment names to their context names.
+   *
+   * @return array
+   *   A mapping of the expected assignment names to their context names. For
+   *   example, if one of the $contexts is named 'current_user', but the plugin
+   *   expects a context named 'user', then this map would contain
+   *   'current_user' => 'user'.
+   */
+  public function getContextMapping();
+
+  /**
+   * Sets a mapping of the expected assignment names to their context names.
+   *
+   * @param array $context_mapping
+   *   A mapping of the expected assignment names to their context names. For
+   *   example, if one of the $contexts is named 'current_user', but the plugin
+   *   expects a context named 'user', then this map would contain
+   *   'current_user' => 'user'.
+   *
+   * @return $this
+   */
+  public function setContextMapping(array $context_mapping);
+
 }
diff --git a/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
index 1a6b00afb885..987bab95af97 100644
--- a/core/lib/Drupal/Core/Annotation/ContextDefinition.php
+++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Annotation;
 
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\StringTranslation\TranslationWrapper;
 
 /**
  * @defgroup plugin_context Annotation for context definition
@@ -94,9 +95,18 @@ public function __construct(array $values) {
     $values += array(
       'required' => TRUE,
       'multiple' => FALSE,
-      'label' => NULL,
-      'description' => NULL,
     );
+    // Annotation classes extract data from passed annotation classes directly
+    // 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) {
+        $values[$key] = (string) $values[$key]->get();
+      }
+      else {
+        $values[$key] = NULL;
+      }
+    }
     if (isset($values['class']) && !in_array('Drupal\Core\Plugin\Context\ContextDefinitionInterface', class_implements($values['class']))) {
       throw new \Exception('ContextDefinition class must implement \Drupal\Core\Plugin\Context\ContextDefinitionInterface.');
     }
diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php
index 2be36701cc32..ba1026b55649 100644
--- a/core/lib/Drupal/Core/Block/BlockBase.php
+++ b/core/lib/Drupal/Core/Block/BlockBase.php
@@ -8,13 +8,7 @@
 namespace Drupal\Core\Block;
 
 use Drupal\block\BlockInterface;
-use Drupal\block\Event\BlockConditionContextEvent;
-use Drupal\block\Event\BlockEvents;
-use Drupal\Component\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Condition\ConditionAccessResolverTrait;
-use Drupal\Core\Condition\ConditionPluginCollection;
-use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContextAwarePluginBase;
 use Drupal\Component\Utility\Unicode;
@@ -35,22 +29,6 @@
  */
 abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
 
-  use ConditionAccessResolverTrait;
-
-  /**
-   * The condition plugin collection.
-   *
-   * @var \Drupal\Core\Condition\ConditionPluginCollection
-   */
-  protected $conditionCollection;
-
-  /**
-   * The condition plugin manager.
-   *
-   * @var \Drupal\Core\Executable\ExecutableManagerInterface
-   */
-  protected $conditionPluginManager;
-
   /**
    * The transliteration service.
    *
@@ -84,9 +62,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
    * {@inheritdoc}
    */
   public function getConfiguration() {
-    return array(
-      'visibility' => $this->getVisibilityConditions()->getConfiguration(),
-    ) + $this->configuration;
+    return $this->configuration;
   }
 
   /**
@@ -107,13 +83,6 @@ public function setConfiguration(array $configuration) {
    *   An associative array with the default configuration.
    */
   protected function baseConfigurationDefaults() {
-    // @todo Allow list of conditions to be configured in
-    //   https://drupal.org/node/2284687.
-    $visibility = array_map(function ($definition) {
-      return array('id' => $definition['id']);
-    }, $this->conditionPluginManager()->getDefinitions());
-    unset($visibility['current_theme']);
-
     return array(
       'id' => $this->getPluginId(),
       'label' => '',
@@ -123,7 +92,6 @@ protected function baseConfigurationDefaults() {
         'max_age' => 0,
         'contexts' => array(),
       ),
-      'visibility' => $visibility,
     );
   }
 
@@ -152,25 +120,10 @@ public function calculateDependencies() {
    * {@inheritdoc}
    */
   public function access(AccountInterface $account) {
-    // @todo Add in a context mapping until the UI supports configuring them,
-    //   see https://drupal.org/node/2284687.
-    $mappings['user_role']['current_user'] = 'user';
-
-    $conditions = $this->getVisibilityConditions();
-    $contexts = $this->getConditionContexts();
-    foreach ($conditions as $condition_id => $condition) {
-      if ($condition instanceof ContextAwarePluginInterface) {
-        if (!isset($mappings[$condition_id])) {
-          $mappings[$condition_id] = array();
-        }
-        $this->contextHandler()->applyContextMapping($condition, $contexts, $mappings[$condition_id]);
-      }
-    }
-    // This should not be hardcoded to an uncacheable access check result, but
-    // in order to fix that, we need condition plugins to return cache contexts,
-    // otherwise it will be impossible to determine by which cache contexts the
-    // result should be varied.
-    if ($this->resolveConditions($conditions, 'and', $contexts, $mappings) !== FALSE && $this->blockAccess($account)) {
+    // @todo Remove self::blockAccess() and force individual plugins to return
+    //   their own AccessResult logic. Until that is done in
+    //   https://www.drupal.org/node/2375689 the access will be set uncacheable.
+    if ($this->blockAccess($account)) {
       $access = AccessResult::allowed();
     }
     else {
@@ -179,18 +132,6 @@ public function access(AccountInterface $account) {
     return $access->setCacheable(FALSE);
   }
 
-  /**
-   * Gets the values for all defined contexts.
-   *
-   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
-   *   An array of set contexts, keyed by context name.
-   */
-  protected function getConditionContexts() {
-    $conditions = $this->getVisibilityConditions();
-    $this->eventDispatcher()->dispatch(BlockEvents::CONDITION_CONTEXT, new BlockConditionContextEvent($conditions));
-    return $conditions->getConditionContexts();
-  }
-
   /**
    * Indicates whether the block should be shown.
    *
@@ -287,53 +228,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
       $form['cache']['contexts']['#description'] .= ' ' . t('This block is <em>always</em> varied by the following contexts: %required-context-list.', array('%required-context-list' => $required_context_list));
     }
 
-    $form['visibility_tabs'] = array(
-      '#type' => 'vertical_tabs',
-      '#title' => $this->t('Visibility'),
-      '#parents' => array('visibility_tabs'),
-      '#attached' => array(
-        'library' => array(
-          'block/drupal.block',
-        ),
-      ),
-    );
-    foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
-      $condition_form = $condition->buildConfigurationForm(array(), $form_state);
-      $condition_form['#type'] = 'details';
-      $condition_form['#title'] = $condition->getPluginDefinition()['label'];
-      $condition_form['#group'] = 'visibility_tabs';
-      $form['visibility'][$condition_id] = $condition_form;
-    }
-
-    // @todo Determine if there is a better way to rename the conditions.
-    if (isset($form['visibility']['node_type'])) {
-      $form['visibility']['node_type']['#title'] = $this->t('Content types');
-      $form['visibility']['node_type']['bundles']['#title'] = $this->t('Content types');
-      $form['visibility']['node_type']['negate']['#type'] = 'value';
-      $form['visibility']['node_type']['negate']['#title_display'] = 'invisible';
-      $form['visibility']['node_type']['negate']['#value'] = $form['visibility']['node_type']['negate']['#default_value'];
-    }
-    if (isset($form['visibility']['user_role'])) {
-      $form['visibility']['user_role']['#title'] = $this->t('Roles');
-      unset($form['visibility']['user_role']['roles']['#description']);
-      $form['visibility']['user_role']['negate']['#type'] = 'value';
-      $form['visibility']['user_role']['negate']['#value'] = $form['visibility']['user_role']['negate']['#default_value'];
-    }
-    if (isset($form['visibility']['request_path'])) {
-      $form['visibility']['request_path']['#title'] = $this->t('Pages');
-      $form['visibility']['request_path']['negate']['#type'] = 'radios';
-      $form['visibility']['request_path']['negate']['#title_display'] = 'invisible';
-      $form['visibility']['request_path']['negate']['#default_value'] = (int) $form['visibility']['request_path']['negate']['#default_value'];
-      $form['visibility']['request_path']['negate']['#options'] = array(
-        $this->t('Show for the listed pages'),
-        $this->t('Hide for the listed pages'),
-      );
-    }
-    if (isset($form['visibility']['language'])) {
-      $form['visibility']['language']['negate']['#type'] = 'value';
-      $form['visibility']['language']['negate']['#value'] = $form['visibility']['language']['negate']['#default_value'];
-    }
-
     // Add plugin-specific settings for this block type.
     $form += $this->blockForm($form, $form_state);
     return $form;
@@ -362,15 +256,6 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
     $contexts = $form_state->getValue(array('cache', 'contexts'));
     $form_state->setValue(array('cache', 'contexts'), array_values(array_filter($contexts)));
 
-    foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
-      // Allow the condition to validate the form.
-      $condition_values = (new FormState())
-        ->setValues($form_state->getValue(['visibility', $condition_id]));
-      $condition->validateConfigurationForm($form, $condition_values);
-      // Update the original form values.
-      $form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
-    }
-
     $this->blockValidate($form, $form_state);
   }
 
@@ -394,14 +279,6 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
       $this->configuration['label_display'] = $form_state->getValue('label_display');
       $this->configuration['provider'] = $form_state->getValue('provider');
       $this->configuration['cache'] = $form_state->getValue('cache');
-      foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
-        // Allow the condition to submit the form.
-        $condition_values = (new FormState())
-          ->setValues($form_state->getValue(['visibility', $condition_id]));
-        $condition->submitConfigurationForm($form, $condition_values);
-        // Update the original form values.
-        $form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
-      }
       $this->blockSubmit($form, $form_state);
     }
   }
@@ -503,61 +380,4 @@ public function isCacheable() {
     return $max_age === Cache::PERMANENT || $max_age > 0;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getVisibilityConditions() {
-    if (!isset($this->conditionCollection)) {
-      $this->conditionCollection = new ConditionPluginCollection($this->conditionPluginManager(), $this->configuration['visibility']);
-    }
-    return $this->conditionCollection;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getVisibilityCondition($instance_id) {
-    return $this->getVisibilityConditions()->get($instance_id);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setVisibilityConfig($instance_id, array $configuration) {
-    $this->getVisibilityConditions()->setInstanceConfiguration($instance_id, $configuration);
-    return $this;
-  }
-
-  /**
-   * Gets the condition plugin manager.
-   *
-   * @return \Drupal\Core\Executable\ExecutableManagerInterface
-   *   The condition plugin manager.
-   */
-  protected function conditionPluginManager() {
-    if (!isset($this->conditionPluginManager)) {
-      $this->conditionPluginManager = \Drupal::service('plugin.manager.condition');
-    }
-    return $this->conditionPluginManager;
-  }
-
-  /**
-   * Wraps the event dispatcher.
-   *
-   * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
-   *   The event dispatcher.
-   */
-  protected function eventDispatcher() {
-    return \Drupal::service('event_dispatcher');
-  }
-
-  /**
-   * Wraps the context handler.
-   *
-   * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
-   */
-  protected function contextHandler() {
-    return \Drupal::service('context.handler');
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Block/BlockPluginInterface.php b/core/lib/Drupal/Core/Block/BlockPluginInterface.php
index 01f97adc98c6..f63a95589ecc 100644
--- a/core/lib/Drupal/Core/Block/BlockPluginInterface.php
+++ b/core/lib/Drupal/Core/Block/BlockPluginInterface.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\Core\Block;
 
-use Drupal\Component\Plugin\Context\ContextInterface;
 use Drupal\Component\Plugin\DerivativeInspectionInterface;
 use Drupal\Core\Cache\CacheableInterface;
 use Drupal\Component\Plugin\PluginInspectionInterface;
@@ -143,35 +142,4 @@ public function blockSubmit($form, FormStateInterface $form_state);
    */
   public function getMachineNameSuggestion();
 
-  /**
-   * Gets conditions for this block.
-   *
-   * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
-   *   An array or collection of configured condition plugins.
-   */
-  public function getVisibilityConditions();
-
-  /**
-   * Gets a visibility condition plugin instance.
-   *
-   * @param string $instance_id
-   *   The condition plugin instance ID.
-   *
-   * @return \Drupal\Core\Condition\ConditionInterface
-   *   A condition plugin.
-   */
-  public function getVisibilityCondition($instance_id);
-
-  /**
-   * Sets the visibility condition configuration.
-   *
-   * @param string $instance_id
-   *   The condition instance ID.
-   * @param array $configuration
-   *   The condition configuration.
-   *
-   * @return $this
-   */
-  public function setVisibilityConfig($instance_id, array $configuration);
-
 }
diff --git a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php
index ed28d69a73a2..ece4e205b53d 100644
--- a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php
+++ b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Executable\ExecutablePluginBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
 
 /**
  * Provides a basis for fulfilling contexts for condition plugins.
@@ -21,6 +22,8 @@
  */
 abstract class ConditionPluginBase extends ExecutablePluginBase implements ConditionInterface {
 
+  use ContextAwarePluginAssignmentTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -41,6 +44,9 @@ public function isNegated() {
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $temporary = $form_state->getTemporary();
+    $contexts = isset($temporary['gathered_contexts']) ? $temporary['gathered_contexts'] : [];
+    $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
     $form['negate'] = array(
       '#type' => 'checkbox',
       '#title' => $this->t('Negate the condition'),
diff --git a/core/lib/Drupal/Core/Condition/ConditionPluginCollection.php b/core/lib/Drupal/Core/Condition/ConditionPluginCollection.php
index c2e532f15893..97392fe421bd 100644
--- a/core/lib/Drupal/Core/Condition/ConditionPluginCollection.php
+++ b/core/lib/Drupal/Core/Condition/ConditionPluginCollection.php
@@ -41,6 +41,12 @@ public function getConfiguration() {
       $default_config = array();
       $default_config['id'] = $instance_id;
       $default_config += $this->get($instance_id)->defaultConfiguration();
+      // In order to determine if a plugin is configured, we must compare it to
+      // its default configuration. The default configuration of a plugin does
+      // not contain context_mapping and it is not used when the plugin is not
+      // configured, so remove the context_mapping from the instance config to
+      // compare the remaining values.
+      unset($instance_config['context_mapping']);
       if ($default_config === $instance_config) {
         unset($configuration[$instance_id]);
       }
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 301b5ae3d2f3..a078aee36c11 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -291,10 +291,17 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
+    $values = $form_state->getValues();
+
+    if ($this->entity instanceof EntityWithPluginCollectionInterface) {
+      // Do not manually update values represented by plugin collections.
+      $values = array_diff_key($values, $this->entity->getPluginCollections());
+    }
+
     // @todo: This relies on a method that only exists for config and content
     //   entities, in a different way. Consider moving this logic to a config
     //   entity specific implementation.
-    foreach ($form_state->getValues() as $key => $value) {
+    foreach ($values as $key => $value) {
       $entity->set($key, $value);
     }
   }
diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
index a5a5a3a6fa2e..019ad4e61e06 100644
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\Core\Plugin\Context;
 
-use Drupal\Component\Plugin\ConfigurablePluginInterface;
 use Drupal\Component\Plugin\ContextAwarePluginInterface;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Component\Utility\String;
@@ -73,12 +72,7 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
    * {@inheritdoc}
    */
   public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array()) {
-    if ($plugin instanceof ConfigurablePluginInterface) {
-      $configuration = $plugin->getConfiguration();
-      if (isset($configuration['context_mapping'])) {
-        $mappings += array_flip($configuration['context_mapping']);
-      }
-    }
+    $mappings += $plugin->getContextMapping();
     $plugin_contexts = $plugin->getContextDefinitions();
     // Loop through each context and set it on the plugin if it matches one of
     // the contexts expected by the plugin.
diff --git a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
new file mode 100644
index 000000000000..57899ff88255
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait.
+ */
+
+namespace Drupal\Core\Plugin;
+
+use Drupal\Component\Plugin\ContextAwarePluginInterface;
+
+/**
+ * Handles context assignments for context-aware plugins.
+ */
+trait ContextAwarePluginAssignmentTrait {
+
+  /**
+   * Ensures the t() method is available.
+   *
+   * @see \Drupal\Core\StringTranslation\StringTranslationTrait
+   */
+  abstract protected function t($string, array $args = array(), array $options = array());
+
+  /**
+   * Wraps the context handler.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected function contextHandler() {
+    return \Drupal::service('context.handler');
+  }
+
+  /**
+   * Builds a form element for assigning a context to a given slot.
+   *
+   * @param \Drupal\Component\Plugin\ContextAwarePluginInterface $plugin
+   *   The context-aware plugin.
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts.
+   *
+   * @return array
+   *   A form element for assigning context.
+   */
+  protected function addContextAssignmentElement(ContextAwarePluginInterface $plugin, array $contexts) {
+    $element = [];
+    foreach ($plugin->getContextDefinitions() as $context_slot => $definition) {
+      $valid_contexts = $this->contextHandler()->getMatchingContexts($contexts, $definition);
+      $options = [];
+      foreach ($valid_contexts as $context_id => $context) {
+        $element['#tree'] = TRUE;
+        $options[$context_id] = $context->getContextDefinition()->getLabel();
+        $element[$context_slot] = [
+          '#type' => 'value',
+          '#value' => $context_id,
+        ];
+      }
+
+      if (count($options) > 1) {
+        $assignments = $plugin->getContextMapping();
+        $element[$context_slot] = [
+          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#type' => 'select',
+          '#options' => $options,
+          '#required' => $definition->isRequired(),
+          '#default_value' => !empty($assignments[$context_slot]) ? $assignments[$context_slot] : '',
+        ];
+      }
+    }
+    return $element;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
index 1fec19384259..3cfa67819d25 100644
--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Plugin;
 
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
 use Drupal\Component\Plugin\ContextAwarePluginBase as ComponentContextAwarePluginBase;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
@@ -49,4 +50,45 @@ public function setContext($name, ComponentContextInterface $context) {
     parent::setContext($name, $context);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getContextMapping() {
+    $configuration = $this instanceof ConfigurablePluginInterface ? $this->getConfiguration() : $this->configuration;
+    return isset($configuration['context_mapping']) ? array_flip($configuration['context_mapping']) : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContextMapping(array $context_mapping) {
+    if ($this instanceof ConfigurablePluginInterface) {
+      $configuration = $this->getConfiguration();
+      $configuration['context_mapping'] = $context_mapping;
+      $this->setConfiguration($configuration);
+    }
+    else {
+      $this->configuration['context_mapping'] = $context_mapping;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface[]
+   */
+  public function getContextDefinitions() {
+    return parent::getContextDefinitions();
+  }
+
+  /**
+   * Wraps the context handler.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected function contextHandler() {
+    return \Drupal::service('context.handler');
+  }
+
 }
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index a294dc806a31..6506ab208fe4 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -301,7 +301,7 @@ function block_configurable_language_delete(ConfigurableLanguageInterface $langu
     $visibility = $block->getVisibility();
     if (isset($visibility['language']['langcodes'][$language->id()])) {
       unset($visibility['language']['langcodes'][$language->id()]);
-      $block->getPlugin()->setVisibilityConfig('language', $visibility['language']);
+      $block->setVisibilityConfig('language', $visibility['language']);
       $block->save();
     }
   }
diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml
index 6442712a5cf0..df4d0d7f8121 100644
--- a/core/modules/block/block.services.yml
+++ b/core/modules/block/block.services.yml
@@ -24,4 +24,4 @@ services:
       - { name: 'event_subscriber' }
   block.repository:
     class: Drupal\block\BlockRepository
-    arguments: ['@entity.manager', '@theme.manager']
+    arguments: ['@entity.manager', '@theme.manager', '@context.handler']
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index c142f898f757..1e6b8235746a 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -24,6 +24,12 @@ block.block.*:
       label: 'Plugin'
     settings:
       type: block.settings.[%parent.plugin]
+    visibility:
+      type: sequence
+      label: 'Visibility Conditions'
+      sequence:
+        - type: condition.plugin.[id]
+          label: 'Visibility Condition'
 
 block.settings.*:
   type: block_settings
diff --git a/core/modules/block/js/block.js b/core/modules/block/js/block.js
index 3affe12afb15..c25842f20235 100644
--- a/core/modules/block/js/block.js
+++ b/core/modules/block/js/block.js
@@ -26,10 +26,10 @@
         return vals.join(', ');
       }
 
-      $('#edit-settings-visibility-node-type, #edit-settings-visibility-language, #edit-settings-visibility-user-role').drupalSetSummary(checkboxesSummary);
+      $('#edit-visibility-node-type, #edit-visibility-language, #edit-visibility-user-role').drupalSetSummary(checkboxesSummary);
 
-      $('#edit-settings-visibility-request-path').drupalSetSummary(function (context) {
-        var $pages = $(context).find('textarea[name="settings[visibility][request_path][pages]"]');
+      $('#edit-visibility-request-path').drupalSetSummary(function (context) {
+        var $pages = $(context).find('textarea[name="visibility[request_path][pages]"]');
         if (!$pages.val()) {
           return Drupal.t('Not restricted');
         }
diff --git a/core/modules/block/src/BlockAccessControlHandler.php b/core/modules/block/src/BlockAccessControlHandler.php
index 8bfa3656f463..82318c16c18e 100644
--- a/core/modules/block/src/BlockAccessControlHandler.php
+++ b/core/modules/block/src/BlockAccessControlHandler.php
@@ -7,23 +7,75 @@
 
 namespace Drupal\block;
 
+use Drupal\Component\Plugin\ContextAwarePluginInterface;
+use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Condition\ConditionAccessResolverTrait;
 use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityHandlerInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Executable\ExecutableManagerInterface;
+use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines the access control handler for the block entity type.
  *
  * @see \Drupal\block\Entity\Block
  */
-class BlockAccessControlHandler extends EntityAccessControlHandler {
+class BlockAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
+
+  use ConditionAccessResolverTrait;
+
+  /**
+   * The condition plugin manager.
+   *
+   * @var \Drupal\Core\Executable\ExecutableManagerInterface
+   */
+  protected $manager;
+
+  /**
+   * The plugin context handler.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected $contextHandler;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('plugin.manager.condition'),
+      $container->get('context.handler')
+    );
+  }
+
+  /**
+   * Constructs the block access control handler instance
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
+   *   The ConditionManager for checking visibility of blocks.
+   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
+   *   The ContextHandler for applying contexts to conditions properly.
+   */
+  public function __construct(EntityTypeInterface $entity_type, ExecutableManagerInterface $manager, ContextHandlerInterface $context_handler) {
+    parent::__construct($entity_type);
+    $this->manager = $manager;
+    $this->contextHandler = $context_handler;
+  }
+
 
   /**
    * {@inheritdoc}
    */
   protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
-    /** @var $entity \Drupal\block\BlockInterface */
+    /** @var \Drupal\block\BlockInterface $entity */
     if ($operation != 'view') {
       return parent::checkAccess($entity, $operation, $langcode, $account);
     }
@@ -33,8 +85,33 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
       return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
     }
     else {
-      // Delegate to the plugin.
-      return $entity->getPlugin()->access($account)->cacheUntilEntityChanges($entity);
+      $contexts = $entity->getContexts();
+      $conditions = [];
+      foreach ($entity->getVisibilityConditions() as $condition_id => $condition) {
+        if ($condition instanceof ContextAwarePluginInterface) {
+          try {
+            $this->contextHandler->applyContextMapping($condition, $contexts);
+          }
+          catch (ContextException $e) {
+            return AccessResult::forbidden()->setCacheable(FALSE);
+          }
+        }
+        $conditions[$condition_id] = $condition;
+      }
+      if ($this->resolveConditions($conditions, 'and') !== FALSE) {
+        // Delegate to the plugin.
+        $access = $entity->getPlugin()->access($account);
+      }
+      else {
+        $access = AccessResult::forbidden();
+      }
+      // This should not be hardcoded to an uncacheable access check result, but
+      // in order to fix that, we need condition plugins to return cache contexts,
+      // otherwise it will be impossible to determine by which cache contexts the
+      // result should be varied.
+      // @todo Change this to use $access->cacheUntilEntityChanges($entity) once
+      //   https://www.drupal.org/node/2375695 is resolved.
+      return $access->setCacheable(FALSE);
     }
   }
 
diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php
index 73e7ea4bb1f1..d2dc42ffeea1 100644
--- a/core/modules/block/src/BlockForm.php
+++ b/core/modules/block/src/BlockForm.php
@@ -7,11 +7,17 @@
 
 namespace Drupal\block;
 
+use Drupal\block\Event\BlockContextEvent;
+use Drupal\block\Event\BlockEvents;
+use Drupal\Component\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Executable\ExecutableManagerInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Provides form for block instance forms.
@@ -32,14 +38,44 @@ class BlockForm extends EntityForm {
    */
   protected $storage;
 
+  /**
+   * The condition plugin manager.
+   *
+   * @var \Drupal\Core\Condition\ConditionManager
+   */
+  protected $manager;
+
+  /**
+   * The event dispatcher service.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
+   * The language manager service.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $language;
+
   /**
    * Constructs a BlockForm object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Drupal\Core\Executable\ExecutableManagerInterface $manager
+   *   The ConditionManager for building the visibility UI.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+   *   The EventDispatcher for gathering administrative contexts.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language
+   *   The language manager.
    */
-  public function __construct(EntityManagerInterface $entity_manager) {
+  public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, EventDispatcherInterface $dispatcher, LanguageManagerInterface $language) {
     $this->storage = $entity_manager->getStorage('block');
+    $this->manager = $manager;
+    $this->dispatcher = $dispatcher;
+    $this->language = $language;
   }
 
   /**
@@ -47,7 +83,10 @@ public function __construct(EntityManagerInterface $entity_manager) {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('entity.manager')
+      $container->get('entity.manager'),
+      $container->get('plugin.manager.condition'),
+      $container->get('event_dispatcher'),
+      $container->get('language_manager')
     );
   }
 
@@ -63,8 +102,15 @@ public function form(array $form, FormStateInterface $form_state) {
     }
     $form_state->set('block_theme', $theme);
 
+    // Store the gathered contexts in the form state for other objects to use
+    // during form building.
+    $temporary = $form_state->getTemporary();
+    $temporary['gathered_contexts'] = $this->dispatcher->dispatch(BlockEvents::ADMINISTRATIVE_CONTEXT, new BlockContextEvent())->getContexts();
+    $form_state->setTemporary($temporary);
+
     $form['#tree'] = TRUE;
     $form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
+    $form['visibility'] = $this->buildVisibilityInterface([], $form_state);
 
     // If creating a new block, calculate a safe default machine name.
     $form['id'] = array(
@@ -132,6 +178,79 @@ public function themeSwitch($form, FormStateInterface $form_state) {
     return $form['region'];
   }
 
+  /**
+   * Helper function for building the visibility UI form.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The form array with the visibility UI added in.
+   */
+  protected function buildVisibilityInterface(array $form, FormStateInterface $form_state) {
+    $form['visibility_tabs'] = [
+      '#type' => 'vertical_tabs',
+      '#title' => $this->t('Visibility'),
+      '#parents' => ['visibility_tabs'],
+      '#attached' => [
+        'library' => [
+          'block/drupal.block',
+        ],
+      ],
+    ];
+    // @todo Allow list of conditions to be configured in
+    //   https://drupal.org/node/2284687.
+    $visibility = $this->entity->getVisibility();
+    foreach ($this->manager->getDefinitions() as $condition_id => $definition) {
+      // Don't display the current theme condition.
+      if ($condition_id == 'current_theme') {
+        continue;
+      }
+      // Don't display the language condition until we have multiple languages.
+      if ($condition_id == 'language' && !$this->language->isMultilingual()) {
+        continue;
+      }
+      /** @var \Drupal\Core\Condition\ConditionInterface $condition */
+      $condition = $this->manager->createInstance($condition_id, isset($visibility[$condition_id]) ? $visibility[$condition_id] : []);
+      $form_state->set(['conditions', $condition_id], $condition);
+      $condition_form = $condition->buildConfigurationForm([], $form_state);
+      $condition_form['#type'] = 'details';
+      $condition_form['#title'] = $condition->getPluginDefinition()['label'];
+      $condition_form['#group'] = 'visibility_tabs';
+      $form[$condition_id] = $condition_form;
+    }
+
+    if (isset($form['node_type'])) {
+      $form['node_type']['#title'] = $this->t('Content types');
+      $form['node_type']['bundles']['#title'] = $this->t('Content types');
+      $form['node_type']['negate']['#type'] = 'value';
+      $form['node_type']['negate']['#title_display'] = 'invisible';
+      $form['node_type']['negate']['#value'] = $form['node_type']['negate']['#default_value'];
+    }
+    if (isset($form['user_role'])) {
+      $form['user_role']['#title'] = $this->t('Roles');
+      unset($form['user_role']['roles']['#description']);
+      $form['user_role']['negate']['#type'] = 'value';
+      $form['user_role']['negate']['#value'] = $form['user_role']['negate']['#default_value'];
+    }
+    if (isset($form['request_path'])) {
+      $form['request_path']['#title'] = $this->t('Pages');
+      $form['request_path']['negate']['#type'] = 'radios';
+      $form['request_path']['negate']['#title_display'] = 'invisible';
+      $form['request_path']['negate']['#options'] = [
+        $this->t('Show for the listed pages'),
+        $this->t('Hide for the listed pages'),
+      ];
+    }
+    if (isset($form['language'])) {
+      $form['language']['negate']['#type'] = 'value';
+      $form['language']['negate']['#value'] = $form['language']['negate']['#default_value'];
+    }
+    return $form;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -154,6 +273,28 @@ public function validate(array $form, FormStateInterface $form_state) {
     $this->entity->getPlugin()->validateConfigurationForm($form, $settings);
     // Update the original form values.
     $form_state->setValue('settings', $settings->getValues());
+    $this->validateVisibility($form, $form_state);
+  }
+
+  /**
+   * Helper function to independently validate the visibility UI.
+   *
+   * @param array $form
+   *   A nested array form elements comprising the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  protected function validateVisibility(array $form, FormStateInterface $form_state) {
+    // Validate visibility condition settings.
+    foreach ($form_state->getValue('visibility') as $condition_id => $values) {
+      // Allow the condition to validate the form.
+      $condition = $form_state->get(['conditions', $condition_id]);
+      $condition_values = (new FormState())
+        ->setValues($values);
+      $condition->validateConfigurationForm($form, $condition_values);
+      // Update the original form values.
+      $form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
+    }
   }
 
   /**
@@ -173,6 +314,24 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // Update the original form values.
     $form_state->setValue('settings', $settings->getValues());
 
+    // Submit visibility condition settings.
+    foreach ($form_state->getValue('visibility') as $condition_id => $values) {
+      // Allow the condition to submit the form.
+      $condition = $form_state->get(['conditions', $condition_id]);
+      $condition_values = (new FormState())
+        ->setValues($values);
+      $condition->submitConfigurationForm($form, $condition_values);
+      if ($condition instanceof ContextAwarePluginInterface) {
+        $context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
+        $condition->setContextMapping($context_mapping);
+      }
+      // Update the original form values.
+      $condition_configuration = $condition->getConfiguration();
+      $form_state->setValue(['visibility', $condition_id], $condition_configuration);
+      // Update the visibility conditions on the block.
+      $entity->getVisibilityConditions()->addInstanceId($condition_id, $condition_configuration);
+    }
+
     // Save the settings of the plugin.
     $entity->save();
 
diff --git a/core/modules/block/src/BlockInterface.php b/core/modules/block/src/BlockInterface.php
index 432e7994126d..d5dd321ea6c4 100644
--- a/core/modules/block/src/BlockInterface.php
+++ b/core/modules/block/src/BlockInterface.php
@@ -40,4 +40,53 @@ public function getPlugin();
    */
   public function getVisibility();
 
+  /**
+   * Gets conditions for this block.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
+   *   An array or collection of configured condition plugins.
+   */
+  public function getVisibilityConditions();
+
+  /**
+   * Gets a visibility condition plugin instance.
+   *
+   * @param string $instance_id
+   *   The condition plugin instance ID.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface
+   *   A condition plugin.
+   */
+  public function getVisibilityCondition($instance_id);
+
+  /**
+   * Sets the visibility condition configuration.
+   *
+   * @param string $instance_id
+   *   The condition instance ID.
+   * @param array $configuration
+   *   The condition configuration.
+   *
+   * @return $this
+   */
+  public function setVisibilityConfig($instance_id, array $configuration);
+
+  /**
+   * Get all available contexts.
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of set contexts, keyed by context name.
+   */
+  public function getContexts();
+
+  /**
+   * Set the contexts that are available for use within the block entity.
+   *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts to set on the block.
+   *
+   * @return $this
+   */
+  public function setContexts(array $contexts);
+
 }
diff --git a/core/modules/block/src/BlockRepository.php b/core/modules/block/src/BlockRepository.php
index 7e17105fcd01..c11a4fe08020 100644
--- a/core/modules/block/src/BlockRepository.php
+++ b/core/modules/block/src/BlockRepository.php
@@ -8,6 +8,7 @@
 namespace Drupal\block;
 
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
 
 /**
@@ -36,10 +37,13 @@ class BlockRepository implements BlockRepositoryInterface {
    *   The entity manager.
    * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
    *   The theme manager.
+   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
+   *   The plugin context handler.
    */
-  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager) {
+  public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager, ContextHandlerInterface $context_handler) {
     $this->blockStorage = $entity_manager->getStorage('block');
     $this->themeManager = $theme_manager;
+    $this->contextHandler = $context_handler;
   }
 
   /**
@@ -65,13 +69,15 @@ protected function getTheme() {
   /**
    * {@inheritdoc}
    */
-  public function getVisibleBlocksPerRegion() {
+  public function getVisibleBlocksPerRegion(array $contexts) {
     // Build an array of the region names in the right order.
     $empty = array_fill_keys(array_keys($this->getRegionNames()), array());
 
     $full = array();
     foreach ($this->blockStorage->loadByProperties(array('theme' => $this->getTheme())) as $block_id => $block) {
-      if ($block->access('view')) {
+      /** @var \Drupal\block\BlockInterface $block */
+      // Set the contexts on the block before checking access.
+      if ($block->setContexts($contexts)->access('view')) {
         $full[$block->get('region')][$block_id] = $block;
       }
     }
diff --git a/core/modules/block/src/BlockRepositoryInterface.php b/core/modules/block/src/BlockRepositoryInterface.php
index 19cfb5ad0a33..082456f0d367 100644
--- a/core/modules/block/src/BlockRepositoryInterface.php
+++ b/core/modules/block/src/BlockRepositoryInterface.php
@@ -12,10 +12,13 @@ interface BlockRepositoryInterface {
   /**
    * Returns an array of regions and their block entities.
    *
+   * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+   *   An array of contexts to set on the blocks.
+   *
    * @return array
    *   The array is first keyed by region machine name, with the values
    *   containing an array keyed by block ID, with block entities as the values.
    */
-  public function getVisibleBlocksPerRegion();
+  public function getVisibleBlocksPerRegion(array $contexts);
 
 }
diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php
index 6f7f27692caa..0edc9616fcba 100644
--- a/core/modules/block/src/Entity/Block.php
+++ b/core/modules/block/src/Entity/Block.php
@@ -8,6 +8,7 @@
 namespace Drupal\block\Entity;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Condition\ConditionPluginCollection;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\block\BlockPluginCollection;
 use Drupal\block\BlockInterface;
@@ -77,6 +78,13 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
    */
   protected $plugin;
 
+  /**
+   * The visibility settings for this block.
+   *
+   * @var array
+   */
+  protected $visibility = [];
+
   /**
    * The plugin collection that holds the block plugin for this entity.
    *
@@ -84,6 +92,27 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
    */
   protected $pluginCollection;
 
+  /**
+   * The available contexts for this block and its visibility conditions.
+   *
+   * @var array
+   */
+  protected $contexts = [];
+
+  /**
+   * The visibility collection.
+   *
+   * @var \Drupal\Core\Condition\ConditionPluginCollection
+   */
+  protected $visibilityCollection;
+
+  /**
+   * The condition plugin manager.
+   *
+   * @var \Drupal\Core\Executable\ExecutableManagerInterface
+   */
+  protected $conditionPluginManager;
+
   /**
    * {@inheritdoc}
    */
@@ -108,7 +137,10 @@ protected function getPluginCollection() {
    * {@inheritdoc}
    */
   public function getPluginCollections() {
-    return array('settings' => $this->getPluginCollection());
+    return [
+      'settings' => $this->getPluginCollection(),
+      'visibility' => $this->getVisibilityConditions(),
+    ];
   }
 
   /**
@@ -183,11 +215,72 @@ public function getCacheTags() {
     return Cache::mergeTags(parent::getCacheTags(), ['theme:' . $this->theme]);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setContexts(array $contexts) {
+    $this->contexts = $contexts;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContexts() {
+    return $this->contexts;
+  }
+
   /**
    * {@inheritdoc}
    */
   public function getVisibility() {
-    return $this->getPlugin()->getVisibilityConditions()->getConfiguration();
+    return $this->getVisibilityConditions()->getConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setVisibilityConfig($instance_id, array $configuration) {
+    $conditions = $this->getVisibilityConditions();
+    if (!$conditions->has($instance_id)) {
+      $configuration['id'] = $instance_id;
+      $conditions->addInstanceId($instance_id, $configuration);
+    }
+    else {
+      $conditions->setInstanceConfiguration($instance_id, $configuration);
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getVisibilityConditions() {
+    if (!isset($this->visibilityCollection)) {
+      $this->visibilityCollection = new ConditionPluginCollection($this->conditionPluginManager(), $this->get('visibility'));
+    }
+    return $this->visibilityCollection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getVisibilityCondition($instance_id) {
+    return $this->getVisibilityConditions()->get($instance_id);
+  }
+
+  /**
+   * Gets the condition plugin manager.
+   *
+   * @return \Drupal\Core\Executable\ExecutableManagerInterface
+   *   The condition plugin manager.
+   */
+  protected function conditionPluginManager() {
+    $this->conditionPluginManager;
+    if (!isset($this->conditionPluginManager)) {
+      $this->conditionPluginManager = \Drupal::service('plugin.manager.condition');
+    }
+    return $this->conditionPluginManager;
   }
 
 }
diff --git a/core/modules/block/src/Event/BlockConditionContextEvent.php b/core/modules/block/src/Event/BlockConditionContextEvent.php
deleted file mode 100644
index af155b72ad44..000000000000
--- a/core/modules/block/src/Event/BlockConditionContextEvent.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\block\Event\BlockContextEvent.
- */
-
-namespace Drupal\block\Event;
-
-use Drupal\Core\Condition\ConditionPluginCollection;
-use Symfony\Component\EventDispatcher\Event;
-
-/**
- * Wraps block conditions in order for event subscribers to add context.
- *
- * @see \Drupal\block\Event\BlockEvents::CONDITION_CONTEXT
- */
-class BlockConditionContextEvent extends Event {
-
-  /**
-   * @var \Drupal\Core\Condition\ConditionPluginCollection
-   */
-  protected $conditions;
-
-  /**
-   * @param \Drupal\Core\Condition\ConditionPluginCollection $conditions
-   */
-  public function __construct(ConditionPluginCollection $conditions) {
-    $this->conditions = $conditions;
-  }
-
-  /**
-   * @return \Drupal\Core\Block\BlockPluginInterface
-   */
-  public function getConditions() {
-    return $this->conditions;
-  }
-
-}
diff --git a/core/modules/block/src/Event/BlockContextEvent.php b/core/modules/block/src/Event/BlockContextEvent.php
new file mode 100644
index 000000000000..99b0bbdde013
--- /dev/null
+++ b/core/modules/block/src/Event/BlockContextEvent.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Event\BlockContextEvent.
+ */
+
+namespace Drupal\block\Event;
+
+use Drupal\Core\Plugin\Context\ContextInterface;
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Event subscribers can add context to be used by the block and its conditions.
+ *
+ * @see \Drupal\block\Event\BlockEvents::ACTIVE_CONTEXT
+ * @see \Drupal\block\Event\BlockEvents::ADMINISTRATIVE_CONTEXT
+ */
+class BlockContextEvent extends Event {
+
+  /**
+   * The array of available contexts for blocks.
+   *
+   * @var array
+   */
+  protected $contexts = [];
+
+  /**
+   * Sets the context object for a given name.
+   *
+   * @param string $name
+   *   The name to store the context object under.
+   * @param \Drupal\Core\Plugin\Context\ContextInterface $context
+   *   The context object to set.
+   *
+   * @return $this
+   */
+  public function setContext($name, ContextInterface $context) {
+    $this->contexts[$name] = $context;
+    return $this;
+  }
+
+  /**
+   * Returns the context objects.
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of contexts that have been provided.
+   */
+  public function getContexts() {
+    return $this->contexts;
+  }
+
+}
diff --git a/core/modules/block/src/Event/BlockEvents.php b/core/modules/block/src/Event/BlockEvents.php
index 8d71c0702efc..22d3e90dc10d 100644
--- a/core/modules/block/src/Event/BlockEvents.php
+++ b/core/modules/block/src/Event/BlockEvents.php
@@ -16,8 +16,16 @@ final class BlockEvents {
    * Name of the event when gathering condition context for a block plugin.
    *
    * @see \Drupal\Core\Block\BlockBase::getConditionContexts()
-   * @see \Drupal\block\Event\BlockConditionContextEvent
+   * @see \Drupal\block\Event\BlockContextEvent
    */
-  const CONDITION_CONTEXT = 'block.condition_context';
+  const ACTIVE_CONTEXT = 'block.active_context';
+
+  /**
+   * Name of the event when gathering contexts for plugin configuration.
+   *
+   * @see \Drupal\block\BlockForm::form()
+   * @see \Drupal\block\Event\BlockContextEvent
+   */
+  const ADMINISTRATIVE_CONTEXT = 'block.administrative_context';
 
 }
diff --git a/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php b/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php
deleted file mode 100644
index 9fc2e87e34cd..000000000000
--- a/core/modules/block/src/EventSubscriber/BlockConditionContextSubscriberBase.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\block\EventSubscriber\BlockContextSubscriberBase.
- */
-
-namespace Drupal\block\EventSubscriber;
-
-use Drupal\block\Event\BlockConditionContextEvent;
-use Drupal\block\Event\BlockEvents;
-use Drupal\Component\Plugin\Context\ContextInterface;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Provides a base class for block context subscribers.
- */
-abstract class BlockConditionContextSubscriberBase implements EventSubscriberInterface {
-
-  /**
-   * @var \Drupal\Core\Condition\ConditionPluginCollection
-   */
-  protected $conditions;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events[BlockEvents::CONDITION_CONTEXT][] = 'onBlockConditionContext';
-    return $events;
-  }
-
-  /**
-   * Subscribes to the event and delegates to the subclass.
-   */
-  public function onBlockConditionContext(BlockConditionContextEvent $event) {
-    $this->conditions = $event->getConditions();
-    $this->determineBlockContext();
-  }
-
-  /**
-   * Determines the contexts for a given block.
-   */
-  abstract protected function determineBlockContext();
-
-  /**
-   * Sets the condition context for a given name.
-   *
-   * @param string $name
-   *   The name of the context.
-   * @param \Drupal\Component\Plugin\Context\ContextInterface $context
-   *   The context to add.
-   *
-   * @return $this
-   */
-  public function addContext($name, ContextInterface $context) {
-    $this->conditions->addContext($name, $context);
-    return $this;
-  }
-
-}
diff --git a/core/modules/block/src/EventSubscriber/BlockContextSubscriberBase.php b/core/modules/block/src/EventSubscriber/BlockContextSubscriberBase.php
new file mode 100644
index 000000000000..9707edc13998
--- /dev/null
+++ b/core/modules/block/src/EventSubscriber/BlockContextSubscriberBase.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\EventSubscriber\BlockContextSubscriberBase.
+ */
+
+namespace Drupal\block\EventSubscriber;
+
+use Drupal\block\Event\BlockContextEvent;
+use Drupal\block\Event\BlockEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Provides a base class for block context subscribers.
+ */
+abstract class BlockContextSubscriberBase implements EventSubscriberInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[BlockEvents::ACTIVE_CONTEXT][] = 'onBlockActiveContext';
+    $events[BlockEvents::ADMINISTRATIVE_CONTEXT][] = 'onBlockAdministrativeContext';
+    return $events;
+  }
+
+  /**
+   * Determines the available run-time contexts.
+   *
+   * For blocks to render correctly, all of the contexts that they require
+   * must be populated with values. So this method must set a value for each
+   * context that it adds. For example:
+   * @code
+   *   // Determine a specific node to pass as context to blocks.
+   *   $node = ...
+   *
+   *   // Set that specific node as the value of the 'node' context.
+   *   $context = new Context(new ContextDefinition('entity:node'));
+   *   $context->setContextValue($node);
+   *   $event->setContext('node', $context);
+   * @endcode
+   *
+   * @param \Drupal\block\Event\BlockContextEvent $event
+   *   The Event to which to register available contexts.
+   */
+  abstract public function onBlockActiveContext(BlockContextEvent $event);
+
+  /**
+   * Determines the available configuration-time contexts.
+   *
+   * When a block is being configured, the configuration UI must know which
+   * named contexts are potentially available, but does not care about the
+   * value, since the value can be different for each request, and might not
+   * be available at all during the configuration UI's request.
+   *
+   * For example:
+   * @code
+   *   // During configuration, there is no specific node to pass as context.
+   *   // However, inform the system that a context named 'node' is available,
+   *   // and provide its definition, so that blocks can be configured to use
+   *   // it. When the block is rendered, the value of this context will be
+   *   // supplied by onBlockActiveContext().
+   *   $context = new Context(new ContextDefinition('entity:node'));
+   *   $event->setContext('node', $context);
+   * @endcode
+   *
+   * @param \Drupal\block\Event\BlockContextEvent $event
+   *   The Event to which to register available contexts.
+   *
+   * @see static::onBlockActiveContext()
+   */
+  abstract public function onBlockAdministrativeContext(BlockContextEvent $event);
+
+}
diff --git a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
index 458417295c0f..023f7ccfe3d8 100644
--- a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
+++ b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\block\EventSubscriber;
 
+use Drupal\block\Event\BlockContextEvent;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
@@ -15,7 +16,7 @@
 /**
  * Sets the current language as a context.
  */
-class CurrentLanguageContext extends BlockConditionContextSubscriberBase {
+class CurrentLanguageContext extends BlockContextSubscriberBase {
 
   use StringTranslationTrait;
 
@@ -39,10 +40,24 @@ public function __construct(LanguageManagerInterface $language_manager) {
   /**
    * {@inheritdoc}
    */
-  protected function determineBlockContext() {
-    $context = new Context(new ContextDefinition('language', $this->t('Current language')));
-    $context->setContextValue($this->languageManager->getCurrentLanguage());
-    $this->addContext('language', $context);
+  public function onBlockActiveContext(BlockContextEvent $event) {
+    // Add a context for each language type.
+    $language_types = $this->languageManager->getLanguageTypes();
+    $info = $this->languageManager->getDefinedLanguageTypesInfo();
+    foreach ($language_types as $type_key) {
+      if (isset($info[$type_key]['name'])) {
+        $context = new Context(new ContextDefinition('language', $info[$type_key]['name']));
+        $context->setContextValue($this->languageManager->getCurrentLanguage($type_key));
+        $event->setContext('language.' . $type_key, $context);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onBlockAdministrativeContext(BlockContextEvent $event) {
+    $this->onBlockActiveContext($event);
   }
 
 }
diff --git a/core/modules/block/src/EventSubscriber/CurrentUserContext.php b/core/modules/block/src/EventSubscriber/CurrentUserContext.php
index 2a5a19ebc851..cb70f3d5f776 100644
--- a/core/modules/block/src/EventSubscriber/CurrentUserContext.php
+++ b/core/modules/block/src/EventSubscriber/CurrentUserContext.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\block\EventSubscriber;
 
+use Drupal\block\Event\BlockContextEvent;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
@@ -16,7 +17,7 @@
 /**
  * Sets the current user as a context.
  */
-class CurrentUserContext extends BlockConditionContextSubscriberBase {
+class CurrentUserContext extends BlockContextSubscriberBase {
 
   use StringTranslationTrait;
 
@@ -50,12 +51,19 @@ public function __construct(AccountInterface $account, EntityManagerInterface $e
   /**
    * {@inheritdoc}
    */
-  protected function determineBlockContext() {
+  public function onBlockActiveContext(BlockContextEvent $event) {
     $current_user = $this->userStorage->load($this->account->id());
 
     $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')));
     $context->setContextValue($current_user);
-    $this->addContext('current_user', $context);
+    $event->setContext('user.current_user', $context);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onBlockAdministrativeContext(BlockContextEvent $event) {
+    $this->onBlockActiveContext($event);
   }
 
 }
diff --git a/core/modules/block/src/EventSubscriber/NodeRouteContext.php b/core/modules/block/src/EventSubscriber/NodeRouteContext.php
index c1bd2bf273e8..04eb339a7ec8 100644
--- a/core/modules/block/src/EventSubscriber/NodeRouteContext.php
+++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\block\EventSubscriber;
 
+use Drupal\block\Event\BlockContextEvent;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -15,7 +16,7 @@
 /**
  * Sets the current node as a context on node routes.
  */
-class NodeRouteContext extends BlockConditionContextSubscriberBase {
+class NodeRouteContext extends BlockContextSubscriberBase {
 
   /**
    * The route match object.
@@ -37,20 +38,28 @@ public function __construct(RouteMatchInterface $route_match) {
   /**
    * {@inheritdoc}
    */
-  protected function determineBlockContext() {
+  public function onBlockActiveContext(BlockContextEvent $event) {
     if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
       $context = new Context(new ContextDefinition($route_contexts['node']['type']));
       if ($node = $this->routeMatch->getParameter('node')) {
         $context->setContextValue($node);
       }
-      $this->addContext('node', $context);
+      $event->setContext('node.node', $context);
     }
     elseif ($this->routeMatch->getRouteName() == 'node.add') {
       $node_type = $this->routeMatch->getParameter('node_type');
       $context = new Context(new ContextDefinition('entity:node'));
       $context->setContextValue(Node::create(array('type' => $node_type->id())));
-      $this->addContext('node', $context);
+      $event->setContext('node.node', $context);
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function onBlockAdministrativeContext(BlockContextEvent $event) {
+    $context = new Context(new ContextDefinition('entity:node'));
+    $event->setContext('node', $context);
+  }
+
 }
diff --git a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
index fe78cb995ed8..895e2c627220 100644
--- a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -8,12 +8,15 @@
 namespace Drupal\block\Plugin\DisplayVariant;
 
 use Drupal\block\BlockRepositoryInterface;
+use Drupal\block\Event\BlockContextEvent;
+use Drupal\block\Event\BlockEvents;
 use Drupal\Core\Block\MainContentBlockPluginInterface;
 use Drupal\Core\Display\PageVariantInterface;
 use Drupal\Core\Entity\EntityViewBuilderInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Display\VariantBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Provides a page display variant that decorates the main content with blocks.
@@ -66,11 +69,14 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
    *   The block repository.
    * @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
    *   The block view builder.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+   *   The event dispatcher.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, EventDispatcherInterface $dispatcher) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->blockRepository = $block_repository;
     $this->blockViewBuilder = $block_view_builder;
+    $this->dispatcher = $dispatcher;
   }
 
   /**
@@ -82,7 +88,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_id,
       $plugin_definition,
       $container->get('block.repository'),
-      $container->get('entity.manager')->getViewBuilder('block')
+      $container->get('entity.manager')->getViewBuilder('block'),
+      $container->get('event_dispatcher')
     );
   }
 
@@ -102,8 +109,9 @@ public function build() {
     $main_content_block_displayed = FALSE;
 
     $build = array();
+    $contexts = $this->getActiveBlockContexts();
     // Load all region content assigned via blocks.
-    foreach ($this->blockRepository->getVisibleBlocksPerRegion() as $region => $blocks) {
+    foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $blocks) {
       /** @var $blocks \Drupal\block\BlockInterface[] */
       foreach ($blocks as $key => $block) {
         $block_plugin = $block->getPlugin();
@@ -130,4 +138,14 @@ public function build() {
     return $build;
   }
 
+  /**
+   * Returns an array of context objects to set on the blocks.
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of contexts to set on the blocks.
+   */
+  protected function getActiveBlockContexts() {
+    return $this->dispatcher->dispatch(BlockEvents::ACTIVE_CONTEXT, new BlockContextEvent())->getContexts();
+  }
+
 }
diff --git a/core/modules/block/src/Tests/BlockInterfaceTest.php b/core/modules/block/src/Tests/BlockInterfaceTest.php
index 7361f96f198e..3c7ecb0243b9 100644
--- a/core/modules/block/src/Tests/BlockInterfaceTest.php
+++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
@@ -39,7 +39,6 @@ public function testBlockInterface() {
       'label' => 'Custom Display Message',
     );
     $expected_configuration = array(
-      'visibility' => array(),
       'id' => 'test_block_instantiation',
       'label' => 'Custom Display Message',
       'provider' => 'block_test',
diff --git a/core/modules/block/src/Tests/BlockLanguageTest.php b/core/modules/block/src/Tests/BlockLanguageTest.php
index 380c65c4e2d9..6f22e68b8a8e 100644
--- a/core/modules/block/src/Tests/BlockLanguageTest.php
+++ b/core/modules/block/src/Tests/BlockLanguageTest.php
@@ -28,7 +28,7 @@ class BlockLanguageTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('language', 'block');
+  public static $modules = array('language', 'block', 'content_translation');
 
   protected function setUp() {
     parent::setUp();
@@ -53,11 +53,12 @@ public function testLanguageBlockVisibility() {
     $default_theme = \Drupal::config('system.theme')->get('default');
     $this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme);
 
-    $this->assertField('settings[visibility][language][langcodes][en]', 'Language visibility field is visible.');
+    $this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.');
+    $this->assertNoField('visibility[language][context_mapping][language]', 'Language type field is not visible.');
 
     // Enable a standard block and set the visibility setting for one language.
     $edit = array(
-      'settings[visibility][language][langcodes][en]' => TRUE,
+      'visibility[language][langcodes][en]' => TRUE,
       'id' => strtolower($this->randomMachineName(8)),
       'region' => 'sidebar_first',
     );
@@ -86,7 +87,6 @@ public function testLanguageBlockVisibilityLanguageDelete() {
     $edit = array(
       'visibility' => array(
         'language' => array(
-          'language_type' => 'language_interface',
           'langcodes' => array(
             'fr' => 'fr',
           ),
@@ -97,8 +97,7 @@ public function testLanguageBlockVisibilityLanguageDelete() {
 
     // Check that we have the language in config after saving the setting.
     $visibility = $block->getVisibility();
-    $language = $visibility['language']['langcodes']['fr'];
-    $this->assertTrue('fr' === $language, 'Language is set in the block configuration.');
+    $this->assertEqual('fr', $visibility['language']['langcodes']['fr'], 'Language is set in the block configuration.');
 
     // Delete the language.
     $this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
@@ -108,6 +107,73 @@ public function testLanguageBlockVisibilityLanguageDelete() {
     $block = Block::load($block->id());
     $visibility = $block->getVisibility();
     $this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.');
+
+    // Ensure that the block visibility for language is gone from the UI.
+    $this->drupalGet('admin/structure/block');
+    $this->clickLink('Configure');
+    $elements = $this->xpath('//details[@id="edit-visibility-language"]');
+    $this->assertTrue(empty($elements));
+  }
+
+  /**
+   * Tests block language visibility with different language types.
+   */
+  public function testMultipleLanguageTypes() {
+    // Customize content language settings from their defaults.
+    $edit = [
+      'language_content[configurable]' => TRUE,
+      'language_interface[enabled][language-url]' => FALSE,
+      'language_interface[enabled][language-session]' => TRUE,
+    ];
+    $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
+
+    // Check if the visibility setting is available with a type setting.
+    $default_theme = \Drupal::config('system.theme')->get('default');
+    $this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme);
+    $this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.');
+    $this->assertField('visibility[language][context_mapping][language]', 'Language type field is visible.');
+
+    // Enable a standard block and set visibility to French only.
+    $block_id = strtolower($this->randomMachineName(8));
+    $edit = [
+      'visibility[language][context_mapping][language]' => 'language.language_interface',
+      'visibility[language][langcodes][fr]' => TRUE,
+      'id' => $block_id,
+      'region' => 'sidebar_first',
+    ];
+    $this->drupalPostForm('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block'));
+
+    // Interface negotiation depends on request arguments.
+    $this->drupalGet('node', ['query' => ['language' => 'en']]);
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+    $this->drupalGet('node', ['query' => ['language' => 'fr']]);
+    $this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
+
+    // Content language does not depend on session/request arguments.
+    // It will fall back on English (site default) and not display the block.
+    $this->drupalGet('en');
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+    $this->drupalGet('fr');
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+
+    // Change visibility to now depend on content language for this block.
+    $edit = [
+      'visibility[language][context_mapping][language]' => 'language.language_content'
+    ];
+    $this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
+
+    // Content language negotiation does not depend on request arguments.
+    // It will fall back on English (site default) and not display the block.
+    $this->drupalGet('node', ['query' => ['language' => 'en']]);
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+    $this->drupalGet('node', ['query' => ['language' => 'fr']]);
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+
+    // Content language negotiation depends on path prefix.
+    $this->drupalGet('en');
+    $this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
+    $this->drupalGet('fr');
+    $this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
   }
 
 }
diff --git a/core/modules/block/src/Tests/BlockStorageUnitTest.php b/core/modules/block/src/Tests/BlockStorageUnitTest.php
index 5fb4e8bedef2..6d0c9285fb2a 100644
--- a/core/modules/block/src/Tests/BlockStorageUnitTest.php
+++ b/core/modules/block/src/Tests/BlockStorageUnitTest.php
@@ -26,7 +26,7 @@ class BlockStorageUnitTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('block', 'block_test', 'system');
+  public static $modules = array('block', 'block_test');
 
   /**
    * The block storage.
@@ -94,7 +94,6 @@ protected function createTests() {
       'provider' => NULL,
       'plugin' => 'test_html',
       'settings' => array(
-        'visibility' => array(),
         'id' => 'test_html',
         'label' => '',
         'provider' => 'block_test',
@@ -104,6 +103,7 @@ protected function createTests() {
           'contexts' => array(),
         ),
       ),
+      'visibility' => array(),
     );
 
     $this->assertIdentical($actual_properties, $expected_properties);
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 881cd2bad1fb..3d9a348c2743 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -35,9 +35,9 @@ function testBlockVisibility() {
     );
     // Set the block to be hidden on any user path, and to be shown only to
     // authenticated users.
-    $edit['settings[visibility][request_path][pages]'] = 'user*';
-    $edit['settings[visibility][request_path][negate]'] = TRUE;
-    $edit['settings[visibility][user_role][roles][' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE;
+    $edit['visibility[request_path][pages]'] = 'user*';
+    $edit['visibility[request_path][negate]'] = TRUE;
+    $edit['visibility[user_role][roles][' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE;
     $this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
     $this->assertText('The block configuration has been saved.', 'Block was saved');
 
@@ -57,6 +57,42 @@ function testBlockVisibility() {
     $this->assertNoRaw('sidebar-first', 'Empty sidebar-first region is not displayed.');
   }
 
+  /**
+   * Tests that visibility can be properly toggled.
+   */
+  public function testBlockToggleVisibility() {
+    $block_name = 'system_powered_by_block';
+    // Create a random title for the block.
+    $title = $this->randomMachineName(8);
+    // Enable a standard block.
+    $default_theme = \Drupal::config('system.theme')->get('default');
+    $edit = array(
+      'id' => strtolower($this->randomMachineName(8)),
+      'region' => 'sidebar_first',
+      'settings[label]' => $title,
+    );
+    $block_id = $edit['id'];
+    // Set the block to be shown only to authenticated users.
+    $edit['visibility[user_role][roles][' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE;
+    $this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
+    $this->clickLink('Configure');
+    $this->assertFieldChecked('edit-visibility-user-role-roles-authenticated');
+
+    $edit = [
+      'visibility[user_role][roles][' . DRUPAL_AUTHENTICATED_RID . ']' => FALSE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save block');
+    $this->clickLink('Configure');
+    $this->assertNoFieldChecked('edit-visibility-user-role-roles-authenticated');
+
+    // Ensure that no visibility is configured.
+    /** @var \Drupal\block\BlockInterface $block */
+    $block = Block::load($block_id);
+    $visibility_config = $block->getVisibilityConditions()->getConfiguration();
+    $this->assertIdentical([], $visibility_config);
+    $this->assertIdentical([], $block->get('visibility'));
+  }
+
   /**
    * Test block visibility when leaving "pages" textarea empty.
    */
@@ -70,7 +106,7 @@ function testBlockVisibilityListedEmpty() {
       'id' => strtolower($this->randomMachineName(8)),
       'region' => 'sidebar_first',
       'settings[label]' => $title,
-      'settings[visibility][request_path][negate]' => TRUE,
+      'visibility[request_path][negate]' => TRUE,
     );
     // Set the block to be hidden on any user path, and to be shown only to
     // authenticated users.
diff --git a/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml b/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml
index 72510d029a02..f52790fcc7d0 100644
--- a/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml
+++ b/core/modules/block/tests/modules/block_test/config/install/block.block.test_block.yml
@@ -6,7 +6,6 @@ langcode: en
 region: '-1'
 plugin: test_html
 settings:
-  visibility: {  }
   label: 'Test HTML block'
   provider: block_test
   label_display: 'hidden'
@@ -15,3 +14,4 @@ dependencies:
     - block_test
   theme:
     - stark
+visibility: {  }
diff --git a/core/modules/block/tests/src/Unit/BlockFormTest.php b/core/modules/block/tests/src/Unit/BlockFormTest.php
index 0860f9355f1e..4f563dbf3b42 100644
--- a/core/modules/block/tests/src/Unit/BlockFormTest.php
+++ b/core/modules/block/tests/src/Unit/BlockFormTest.php
@@ -16,6 +16,59 @@
  */
 class BlockFormTest extends UnitTestCase {
 
+  /**
+   * The condition plugin manager.
+   *
+   * @var \Drupal\Core\Executable\ExecutableManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $conditionManager;
+
+  /**
+   * The block storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $storage;
+
+  /**
+   * The event dispatcher service.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $dispatcher;
+
+  /**
+   * The language manager service.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $language;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
+    $this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+    $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
+    $this->entityManager->expects($this->any())
+      ->method('getStorage')
+      ->will($this->returnValue($this->storage));
+
+  }
+
   /**
    * Tests the unique machine name generator.
    *
@@ -38,22 +91,11 @@ public function testGetUniqueMachineName() {
       ->method('execute')
       ->will($this->returnValue(array('test', 'other_test', 'other_test_1', 'other_test_2')));
 
-    $block_storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
-    $block_storage->expects($this->exactly(5))
+    $this->storage->expects($this->exactly(5))
       ->method('getQuery')
       ->will($this->returnValue($query));
 
-    $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-
-    $entity_manager->expects($this->any())
-      ->method('getStorage')
-      ->will($this->returnValue($block_storage));
-
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
-
-    $config_factory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
-
-    $block_form_controller = new BlockForm($entity_manager, $language_manager, $config_factory);
+    $block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->dispatcher, $this->language);
 
     // Ensure that the block with just one other instance gets the next available
     // name suggestion.
diff --git a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
index 7f7aec1e9c1e..0c7e0b931436 100644
--- a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
+++ b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Tests\block\Unit;
 
+use Drupal\Component\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\Block\BlockPluginInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -16,46 +18,77 @@
 class BlockRepositoryTest extends UnitTestCase {
 
   /**
-   * Tests the retrieval of block entities.
-   *
-   * @covers ::getVisibleBlocksPerRegion
-   *
-   * @dataProvider providerBlocksConfig
+   * @var \Drupal\block\BlockRepository
+   */
+  protected $blockRepository;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $blockStorage;
+
+  /**
+   * @var string
    */
-  function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blocks) {
-    $theme = $this->randomMachineName();
+  protected $theme;
+
+  /**
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $contextHandler;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
     $active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
       ->disableOriginalConstructor()
       ->getMock();
+    $this->theme = $this->randomMachineName();
     $active_theme->expects($this->atLeastOnce())
       ->method('getName')
-      ->willReturn($theme);
+      ->willReturn($this->theme);
+
     $theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
     $theme_manager->expects($this->once())
       ->method('getActiveTheme')
       ->will($this->returnValue($active_theme));
 
-    $block_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
+    $this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
+    $this->blockStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
     $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
     $entity_manager->expects($this->any())
       ->method('getStorage')
-      ->willReturn($block_storage);
+      ->willReturn($this->blockStorage);
 
-    $block_repository = $this->getMockBuilder('Drupal\block\BlockRepository')
-      ->setConstructorArgs([$entity_manager, $theme_manager])
+    $this->blockRepository = $this->getMockBuilder('Drupal\block\BlockRepository')
+      ->setConstructorArgs([$entity_manager, $theme_manager, $this->contextHandler])
       ->setMethods(['getRegionNames'])
       ->getMock();
-    $block_repository->expects($this->once())
+    $this->blockRepository->expects($this->once())
       ->method('getRegionNames')
       ->willReturn([
         'top' => 'Top',
         'center' => 'Center',
         'bottom' => 'Bottom',
       ]);
+  }
 
+  /**
+   * Tests the retrieval of block entities.
+   *
+   * @covers ::getVisibleBlocksPerRegion
+   *
+   * @dataProvider providerBlocksConfig
+   */
+  public function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blocks) {
     $blocks = [];
     foreach ($blocks_config as $block_id => $block_config) {
       $block = $this->getMock('Drupal\block\BlockInterface');
+      $block->expects($this->once())
+        ->method('setContexts')
+        ->willReturnSelf();
       $block->expects($this->once())
         ->method('access')
         ->will($this->returnValue($block_config[0]));
@@ -69,12 +102,12 @@ function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blo
       $blocks[$block_id] = $block;
     }
 
-    $block_storage->expects($this->once())
+    $this->blockStorage->expects($this->once())
       ->method('loadByProperties')
-      ->with(['theme' => $theme])
+      ->with(['theme' => $this->theme])
       ->willReturn($blocks);
     $result = [];
-    foreach ($block_repository->getVisibleBlocksPerRegion() as $region => $resulting_blocks) {
+    foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
       $result[$region] = [];
       foreach ($resulting_blocks as $plugin_id => $block) {
         $result[$region][] = $plugin_id;
@@ -83,7 +116,6 @@ function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blo
     $this->assertSame($result, $expected_blocks);
   }
 
-
   public function providerBlocksConfig() {
     $blocks_config = array(
       'block1' => array(
@@ -113,4 +145,48 @@ public function providerBlocksConfig() {
     return $test_cases;
   }
 
+  /**
+   * Tests the retrieval of block entities that are context-aware.
+   *
+   * @covers ::getVisibleBlocksPerRegion
+   */
+  public function testGetVisibleBlocksPerRegionWithContext() {
+    $block = $this->getMock('Drupal\block\BlockInterface');
+    $block->expects($this->once())
+      ->method('setContexts')
+      ->willReturnSelf();
+    $block->expects($this->once())
+      ->method('access')
+      ->willReturn(TRUE);
+    $block->expects($this->once())
+      ->method('get')
+      ->with('region')
+      ->willReturn('top');
+    $blocks['block_id'] = $block;
+
+    $contexts = [];
+    $this->blockStorage->expects($this->once())
+      ->method('loadByProperties')
+      ->with(['theme' => $this->theme])
+      ->willReturn($blocks);
+    $result = [];
+    foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $resulting_blocks) {
+      $result[$region] = [];
+      foreach ($resulting_blocks as $plugin_id => $block) {
+        $result[$region][] = $plugin_id;
+      }
+    }
+    $expected = [
+      'top' => [
+        'block_id',
+      ],
+      'center' => [],
+      'bottom' => [],
+    ];
+    $this->assertSame($expected, $result);
+  }
+
+}
+
+interface TestContextAwareBlockInterface extends BlockPluginInterface, ContextAwarePluginInterface {
 }
diff --git a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
index e16d133533f9..0ae38d4f7df8 100644
--- a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
+++ b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
@@ -30,18 +30,18 @@ class BlockPageVariantTest extends UnitTestCase {
   protected $blockViewBuilder;
 
   /**
-   * The current route match.
+   * The event dispatcher.
    *
-   * @var \Drupal\Core\Routing\RouteMatchInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $routeMatch;
+  protected $dispatcher;
 
   /**
-   * The theme negotiator.
+   * The plugin context handler.
    *
-   * @var \Drupal\Core\Theme\ThemeNegotiatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $themeNegotiator;
+  protected $contextHandler;
 
   /**
    * Sets up a display variant plugin for testing.
@@ -57,10 +57,13 @@ class BlockPageVariantTest extends UnitTestCase {
   public function setUpDisplayVariant($configuration = array(), $definition = array()) {
     $this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
     $this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
-    $this->routeMatch = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
-    $this->themeNegotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+    $this->dispatcher->expects($this->any())
+      ->method('dispatch')
+      ->willReturnArgument(1);
+    $this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
     return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
-      ->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->routeMatch, $this->themeNegotiator))
+      ->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->dispatcher, $this->contextHandler))
       ->setMethods(array('getRegionNames'))
       ->getMock();
   }
diff --git a/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml b/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
index b8b7827fa1e7..a39088a4b6d2 100644
--- a/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
+++ b/core/modules/block_content/tests/modules/block_content_test/config/install/block.block.foobargorilla.yml
@@ -12,11 +12,6 @@ weight: null
 provider: null
 plugin: 'block_content:fb5e8434-3617-4a1d-a252-8273e95ec30e'
 settings:
-  visibility:
-    request_path:
-      id: request_path
-      pages: ''
-      negate: false
   id: 'block_content:fb5e8434-3617-4a1d-a252-8273e95ec30e'
   label: 'Foobar Gorilla'
   provider: block_content
@@ -27,3 +22,8 @@ settings:
   status: true
   info: ''
   view_mode: default
+visibility:
+  request_path:
+    id: request_path
+    pages: ''
+    negate: false
diff --git a/core/modules/image/src/Form/ImageStyleEditForm.php b/core/modules/image/src/Form/ImageStyleEditForm.php
index 4edd31c79457..9c4887c06ec4 100644
--- a/core/modules/image/src/Form/ImageStyleEditForm.php
+++ b/core/modules/image/src/Form/ImageStyleEditForm.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\image\Form;
 
-use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
@@ -286,16 +285,4 @@ protected function updateEffectWeights(array $effects) {
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
-    foreach ($form_state->getValues() as $key => $value) {
-      // Do not copy effects here, see self::updateEffectWeights().
-      if ($key != 'effects') {
-        $entity->set($key, $value);
-      }
-    }
-  }
-
 }
diff --git a/core/modules/language/src/Plugin/Condition/Language.php b/core/modules/language/src/Plugin/Condition/Language.php
index 0f46dd5d2459..fa09f3b6adfb 100644
--- a/core/modules/language/src/Plugin/Condition/Language.php
+++ b/core/modules/language/src/Plugin/Condition/Language.php
@@ -31,23 +31,23 @@ class Language extends ConditionPluginBase {
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
     if (\Drupal::languageManager()->isMultilingual()) {
       // Fetch languages.
-      $languages = language_list(LanguageInterface::STATE_CONFIGURABLE);
+      $languages = \Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE);
       $langcodes_options = array();
       foreach ($languages as $language) {
         $langcodes_options[$language->getId()] = $language->getName();
       }
       $form['langcodes'] = array(
         '#type' => 'checkboxes',
-        '#title' => t('Language selection'),
+        '#title' => $this->t('Language selection'),
         '#default_value' => $this->configuration['langcodes'],
         '#options' => $langcodes_options,
-        '#description' => t('Select languages to enforce. If none are selected, all languages will be allowed.'),
+        '#description' => $this->t('Select languages to enforce. If none are selected, all languages will be allowed.'),
       );
     }
     else {
       $form['langcodes'] = array(
         '#type' => 'value',
-        '#value' => $this->configuration['langcodes'],
+        '#default_value' => $this->configuration['langcodes'],
       );
     }
     return parent::buildConfigurationForm($form, $form_state);
diff --git a/core/modules/language/src/Tests/LanguageBlockSettingsVisibilityTest.php b/core/modules/language/src/Tests/LanguageBlockSettingsVisibilityTest.php
index e1f9cb9d3231..cac5b511df28 100644
--- a/core/modules/language/src/Tests/LanguageBlockSettingsVisibilityTest.php
+++ b/core/modules/language/src/Tests/LanguageBlockSettingsVisibilityTest.php
@@ -23,9 +23,9 @@ public function testUnnecessaryLanguageSettingsVisibility() {
     $this->drupalLogin($admin_user);
     $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'hu'), t('Add language'));
     $this->drupalGet('admin/structure/block/add/system_menu_block:admin/stark');
-    $this->assertNoFieldByXPath('//input[@id="edit-settings-visibility-language-langcodes-und"]', NULL, '\'Not specified\' option does not appear at block config, language settings section.');
-    $this->assertNoFieldByXpath('//input[@id="edit-settings-visibility-language-langcodes-zxx"]', NULL, '\'Not applicable\' option does not appear at block config, language settings section.');
-    $this->assertFieldByXPath('//input[@id="edit-settings-visibility-language-langcodes-en"]', NULL, '\'English\' option appears at block config, language settings section.');
-    $this->assertFieldByXpath('//input[@id="edit-settings-visibility-language-langcodes-hu"]', NULL, '\'Hungarian\' option appears at block config, language settings section.');
+    $this->assertNoFieldByXPath('//input[@id="edit-visibility-language-langcodes-und"]', NULL, '\'Not specified\' option does not appear at block config, language settings section.');
+    $this->assertNoFieldByXpath('//input[@id="edit-visibility-language-langcodes-zxx"]', NULL, '\'Not applicable\' option does not appear at block config, language settings section.');
+    $this->assertFieldByXPath('//input[@id="edit-visibility-language-langcodes-en"]', NULL, '\'English\' option appears at block config, language settings section.');
+    $this->assertFieldByXpath('//input[@id="edit-visibility-language-langcodes-hu"]', NULL, '\'Hungarian\' option appears at block config, language settings section.');
   }
 }
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
index 4b15b05e6aa9..fb7f6d9c5a5d 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
@@ -91,8 +91,7 @@ public function testBlockMigration() {
     $this->assertEqual('sidebar_first', $test_block_user->get('region'));
     $this->assertEqual('bartik', $test_block_user->get('theme'));
     $visibility = $test_block_user->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(0, $test_block_user->weight);
 
     $test_block_user_1 = $blocks['user_1'];
@@ -100,8 +99,7 @@ public function testBlockMigration() {
     $this->assertEqual('sidebar_first', $test_block_user_1->get('region'));
     $this->assertEqual('bartik', $test_block_user_1->get('theme'));
     $visibility = $test_block_user_1->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(0, $test_block_user_1->weight);
 
     // Check system block
@@ -110,8 +108,7 @@ public function testBlockMigration() {
     $this->assertEqual('footer', $test_block_system->get('region'));
     $this->assertEqual('bartik', $test_block_system->get('theme'));
     $visibility = $test_block_system->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(-5, $test_block_system->weight);
 
     // Check menu blocks
@@ -120,8 +117,7 @@ public function testBlockMigration() {
     $this->assertEqual('header', $test_block_menu->get('region'));
     $this->assertEqual('bartik', $test_block_menu->get('theme'));
     $visibility = $test_block_menu->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(-5, $test_block_menu->weight);
 
     // Check custom blocks
@@ -130,8 +126,7 @@ public function testBlockMigration() {
     $this->assertEqual('content', $test_block_block->get('region'));
     $this->assertEqual('bartik', $test_block_block->get('theme'));
     $visibility = $test_block_block->getVisibility();
-    $this->assertEqual(FALSE, $visibility['request_path']['negate']);
-    $this->assertEqual('<front>', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(0, $test_block_block->weight);
 
     $test_block_block_1 = $blocks['block_1'];
@@ -139,8 +134,7 @@ public function testBlockMigration() {
     $this->assertEqual('right', $test_block_block_1->get('region'));
     $this->assertEqual('bluemarine', $test_block_block_1->get('theme'));
     $visibility = $test_block_block_1->getVisibility();
-    $this->assertEqual(FALSE, $visibility['request_path']['negate']);
-    $this->assertEqual('node', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(-4, $test_block_block_1->weight);
 
     $test_block_block_2 = $blocks['block_2'];
@@ -148,8 +142,7 @@ public function testBlockMigration() {
     $this->assertEqual('right', $test_block_block_2->get('region'));
     $this->assertEqual('test_theme', $test_block_block_2->get('theme'));
     $visibility = $test_block_block_2->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(-7, $test_block_block_2->weight);
 
     $test_block_block_3 = $blocks['block_3'];
@@ -157,8 +150,7 @@ public function testBlockMigration() {
     $this->assertEqual('left', $test_block_block_3->get('region'));
     $this->assertEqual('test_theme', $test_block_block_3->get('theme'));
     $visibility = $test_block_block_3->getVisibility();
-    $this->assertEqual(TRUE, $visibility['request_path']['negate']);
-    $this->assertEqual('', $visibility['request_path']['pages']);
+    $this->assertTrue(empty($visibility['request_path']));
     $this->assertEqual(-2, $test_block_block_3->weight);
   }
 }
diff --git a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
index 9bc50544aac7..2ff5981e9b99 100644
--- a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
+++ b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
@@ -117,6 +117,9 @@ public function testRecentNodeBlock() {
     $block = $this->drupalPlaceBlock('system_powered_by_block', array(
       'visibility' => array(
         'node_type' => array(
+          'context_mapping' => array(
+            'node' => 'node.node',
+          ),
           'bundles' => array(
             'article' => 'article',
           ),
diff --git a/core/modules/search/src/Tests/SearchBlockTest.php b/core/modules/search/src/Tests/SearchBlockTest.php
index cdc79057aa76..9aca60fe1261 100644
--- a/core/modules/search/src/Tests/SearchBlockTest.php
+++ b/core/modules/search/src/Tests/SearchBlockTest.php
@@ -59,7 +59,7 @@ protected function testSearchFormBlock() {
 
     $visibility = $block->getVisibility();
     $visibility['request_path']['pages'] = 'search';
-    $block->getPlugin()->setVisibilityConfig('request_path', $visibility['request_path']);
+    $block->setVisibilityConfig('request_path', $visibility['request_path']);
 
     $this->submitGetForm('', $terms, t('Search'));
     $this->assertResponse(200);
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 9eda0a0900c9..809a145b00c5 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -382,13 +382,14 @@ protected function drupalPlaceBlock($plugin_id, array $settings = array()) {
         'max_age' => 0,
       ),
     );
-    foreach (array('region', 'id', 'theme', 'plugin', 'weight') as $key) {
+    $values = [];
+    foreach (array('region', 'id', 'theme', 'plugin', 'weight', 'visibility') as $key) {
       $values[$key] = $settings[$key];
       // Remove extra values that do not belong in the settings array.
       unset($settings[$key]);
     }
-    foreach ($settings['visibility'] as $id => $visibility) {
-      $settings['visibility'][$id]['id'] = $id;
+    foreach ($values['visibility'] as $id => $visibility) {
+      $values['visibility'][$id]['id'] = $id;
     }
     $values['settings'] = $settings;
     $block = entity_create('block', $values);
diff --git a/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
index c26ee664760e..2c03f23680ad 100644
--- a/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
@@ -126,10 +126,6 @@ public function testBuild() {
 
     $definition['provider'] = 'views';
     $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storage, $this->account);
-    $reflector = new \ReflectionClass($plugin);
-    $property = $reflector->getProperty('conditionPluginManager');
-    $property->setAccessible(TRUE);
-    $property->setValue($plugin, $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface'));
 
     $this->assertEquals($build, $plugin->build());
   }
@@ -152,10 +148,6 @@ public function testBuildFailed() {
 
     $definition['provider'] = 'views';
     $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storage, $this->account);
-    $reflector = new \ReflectionClass($plugin);
-    $property = $reflector->getProperty('conditionPluginManager');
-    $property->setAccessible(TRUE);
-    $property->setValue($plugin, $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface'));
 
     $this->assertEquals(array(), $plugin->build());
   }
diff --git a/core/profiles/minimal/config/install/block.block.stark_admin.yml b/core/profiles/minimal/config/install/block.block.stark_admin.yml
index 0f3fdd5e335b..ffbc2fb463c2 100644
--- a/core/profiles/minimal/config/install/block.block.stark_admin.yml
+++ b/core/profiles/minimal/config/install/block.block.stark_admin.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: 'system_menu_block:admin'
 settings:
-  visibility: {  }
   label: Administration
   provider: system
   label_display: visible
@@ -22,3 +21,4 @@ dependencies:
     - system
   theme:
     - stark
+visibility: {  }
diff --git a/core/profiles/minimal/config/install/block.block.stark_login.yml b/core/profiles/minimal/config/install/block.block.stark_login.yml
index 9081e1afde07..41eba3ff30c6 100644
--- a/core/profiles/minimal/config/install/block.block.stark_login.yml
+++ b/core/profiles/minimal/config/install/block.block.stark_login.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: user_login_block
 settings:
-  visibility: {  }
   label: 'User login'
   provider: user
   label_display: visible
@@ -15,3 +14,4 @@ dependencies:
     - user
   theme:
     - stark
+visibility: {  }
diff --git a/core/profiles/minimal/config/install/block.block.stark_tools.yml b/core/profiles/minimal/config/install/block.block.stark_tools.yml
index ef8442b08aef..aa82311faea4 100644
--- a/core/profiles/minimal/config/install/block.block.stark_tools.yml
+++ b/core/profiles/minimal/config/install/block.block.stark_tools.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: 'system_menu_block:tools'
 settings:
-  visibility: {  }
   label: Tools
   provider: system
   label_display: visible
@@ -22,3 +21,4 @@ dependencies:
     - system
   theme:
     - stark
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_account_menu.yml b/core/profiles/standard/config/install/block.block.bartik_account_menu.yml
index 14637599b0a3..db8f8ec97f4c 100644
--- a/core/profiles/standard/config/install/block.block.bartik_account_menu.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_account_menu.yml
@@ -6,7 +6,6 @@ langcode: en
 region: secondary_menu
 plugin: 'system_menu_block:account'
 settings:
-  visibility: { }
   id: 'system_menu_block:account'
   label: 'User account menu'
   provider: system
@@ -23,3 +22,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: { }
diff --git a/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml b/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml
index 2128a6149be2..c89b7280d0ec 100644
--- a/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_breadcrumbs.yml
@@ -6,7 +6,6 @@ langcode: en
 region: '-1'
 plugin: system_breadcrumb_block
 settings:
-  visibility: {  }
   id: system_breadcrumb_block
   label: Breadcrumbs
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_content.yml b/core/profiles/standard/config/install/block.block.bartik_content.yml
index d30a7f30cf52..87d320faf379 100644
--- a/core/profiles/standard/config/install/block.block.bartik_content.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_content.yml
@@ -6,7 +6,6 @@ langcode: en
 region: content
 plugin: system_main_block
 settings:
-  visibility: {  }
   id: system_main_block
   label: 'Main page content'
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_footer.yml b/core/profiles/standard/config/install/block.block.bartik_footer.yml
index 41327360fb57..43817fda1e44 100644
--- a/core/profiles/standard/config/install/block.block.bartik_footer.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_footer.yml
@@ -6,7 +6,6 @@ langcode: en
 region: footer
 plugin: 'system_menu_block:footer'
 settings:
-  visibility: {  }
   id: 'system_menu_block:footer'
   label: 'Footer menu'
   provider: system
@@ -23,3 +22,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_help.yml b/core/profiles/standard/config/install/block.block.bartik_help.yml
index 3a2f48a5f703..941fa1bf8399 100644
--- a/core/profiles/standard/config/install/block.block.bartik_help.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_help.yml
@@ -6,7 +6,6 @@ langcode: en
 region: help
 plugin: system_help_block
 settings:
-  visibility: {  }
   id: system_help_block
   label: 'System Help'
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_login.yml b/core/profiles/standard/config/install/block.block.bartik_login.yml
index 6d58c23bcea4..3a5f5433fd6d 100644
--- a/core/profiles/standard/config/install/block.block.bartik_login.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_login.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: user_login_block
 settings:
-  visibility: {  }
   id: user_login_block
   label: 'User login'
   provider: user
@@ -16,3 +15,4 @@ dependencies:
     - user
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_main_menu.yml b/core/profiles/standard/config/install/block.block.bartik_main_menu.yml
index 7f847c285184..3da0571481b0 100644
--- a/core/profiles/standard/config/install/block.block.bartik_main_menu.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_main_menu.yml
@@ -6,7 +6,6 @@ langcode: en
 region: primary_menu
 plugin: 'system_menu_block:main'
 settings:
-  visibility: {}
   id: 'system_menu_block:main'
   label: 'Main navigation'
   provider: system
@@ -23,3 +22,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {}
diff --git a/core/profiles/standard/config/install/block.block.bartik_powered.yml b/core/profiles/standard/config/install/block.block.bartik_powered.yml
index 106c2791a78f..5a9881f1562a 100644
--- a/core/profiles/standard/config/install/block.block.bartik_powered.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_powered.yml
@@ -6,7 +6,6 @@ langcode: en
 region: footer
 plugin: system_powered_by_block
 settings:
-  visibility: {  }
   id: system_powered_by_block
   label: 'Powered by Drupal'
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_search.yml b/core/profiles/standard/config/install/block.block.bartik_search.yml
index ac4130a32bc7..af99dea99c14 100644
--- a/core/profiles/standard/config/install/block.block.bartik_search.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_search.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: search_form_block
 settings:
-  visibility: {  }
   id: search_form_block
   label: Search
   provider: search
@@ -16,3 +15,4 @@ dependencies:
     - search
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.bartik_tools.yml b/core/profiles/standard/config/install/block.block.bartik_tools.yml
index 4b102ebe5076..aa24e61d28c0 100644
--- a/core/profiles/standard/config/install/block.block.bartik_tools.yml
+++ b/core/profiles/standard/config/install/block.block.bartik_tools.yml
@@ -6,7 +6,6 @@ langcode: en
 region: sidebar_first
 plugin: 'system_menu_block:tools'
 settings:
-  visibility: {  }
   id: 'system_menu_block:tools'
   label: Tools
   provider: system
@@ -23,3 +22,4 @@ dependencies:
     - system
   theme:
     - bartik
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml b/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml
index cd7d7acbf8c0..8ce13d8e990e 100644
--- a/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml
+++ b/core/profiles/standard/config/install/block.block.seven_breadcrumbs.yml
@@ -6,7 +6,6 @@ langcode: en
 region: '-1'
 plugin: system_breadcrumb_block
 settings:
-  visibility: {  }
   id: system_breadcrumb_block
   label: Breadcrumbs
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - seven
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_content.yml b/core/profiles/standard/config/install/block.block.seven_content.yml
index 12c2c5fa0d03..dd2bfbec584b 100644
--- a/core/profiles/standard/config/install/block.block.seven_content.yml
+++ b/core/profiles/standard/config/install/block.block.seven_content.yml
@@ -6,7 +6,6 @@ langcode: en
 region: content
 plugin: system_main_block
 settings:
-  visibility: {  }
   id: system_main_block
   label: 'Main page content'
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - seven
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_help.yml b/core/profiles/standard/config/install/block.block.seven_help.yml
index 1cf9fa315cd4..168e7f00374e 100644
--- a/core/profiles/standard/config/install/block.block.seven_help.yml
+++ b/core/profiles/standard/config/install/block.block.seven_help.yml
@@ -6,7 +6,6 @@ langcode: en
 region: help
 plugin: system_help_block
 settings:
-  visibility: {  }
   id: system_help_block
   label: 'System Help'
   provider: system
@@ -16,3 +15,4 @@ dependencies:
     - system
   theme:
     - seven
+visibility: {  }
diff --git a/core/profiles/standard/config/install/block.block.seven_login.yml b/core/profiles/standard/config/install/block.block.seven_login.yml
index 321d92b4d7b8..0a488a798f90 100644
--- a/core/profiles/standard/config/install/block.block.seven_login.yml
+++ b/core/profiles/standard/config/install/block.block.seven_login.yml
@@ -6,7 +6,6 @@ langcode: en
 region: content
 plugin: user_login_block
 settings:
-  visibility: {  }
   id: user_login_block
   label: 'User login'
   provider: user
@@ -16,3 +15,4 @@ dependencies:
     - user
   theme:
     - seven
+visibility: {  }
diff --git a/core/tests/Drupal/Tests/Core/Block/BlockBaseTest.php b/core/tests/Drupal/Tests/Core/Block/BlockBaseTest.php
index 028ea737ca7b..88be05008fef 100644
--- a/core/tests/Drupal/Tests/Core/Block/BlockBaseTest.php
+++ b/core/tests/Drupal/Tests/Core/Block/BlockBaseTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\Tests\Core\Block;
 
 use Drupal\block_test\Plugin\Block\TestBlockInstantiation;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -28,14 +27,6 @@ public function testGetMachineNameSuggestion() {
       ->setMethods(array('readLanguageOverrides'))
       ->getMock();
 
-    $condition_plugin_manager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
-    $condition_plugin_manager->expects($this->atLeastOnce())
-      ->method('getDefinitions')
-      ->will($this->returnValue(array()));
-    $container = new ContainerBuilder();
-    $container->set('plugin.manager.condition', $condition_plugin_manager);
-    \Drupal::setContainer($container);
-
     $config = array();
     $definition = array(
       'admin_label' => 'Admin label',
@@ -55,52 +46,4 @@ public function testGetMachineNameSuggestion() {
     $this->assertEquals('uberawesome', $block_base->getMachineNameSuggestion());
   }
 
-  /**
-   * Tests initializing the condition plugins initialization.
-   */
-  public function testConditionsBagInitialization() {
-    $plugin_manager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
-    $plugin_manager->expects($this->once())
-      ->method('getDefinitions')
-      ->will($this->returnValue(array(
-        'request_path' => array(
-          'id' => 'request_path',
-        ),
-        'user_role' => array(
-          'id' => 'user_role',
-        ),
-        'node_type' => array(
-          'id' => 'node_type',
-        ),
-        'language' => array(
-          'id' => 'language',
-        ),
-      )));
-    $container = new ContainerBuilder();
-    $container->set('plugin.manager.condition', $plugin_manager);
-    \Drupal::setContainer($container);
-    $config = array();
-    $definition = array(
-      'admin_label' => 'Admin label',
-      'provider' => 'block_test',
-    );
-
-    $block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition);
-    $conditions_collection = $block_base->getVisibilityConditions();
-
-    $this->assertEquals(4, $conditions_collection->count(), "There are 4 condition plugins");
-
-    $instance_id = $this->randomMachineName();
-    $pages = 'node/1';
-    $condition_config = array('id' => 'request_path', 'pages' => $pages);
-    $block_base->setVisibilityConfig($instance_id, $condition_config);
-
-    $plugin_manager->expects($this->once())->method('createInstance')
-      ->withAnyParameters()->will($this->returnValue('test'));
-
-    $condition = $block_base->getVisibilityCondition($instance_id);
-
-    $this->assertEquals('test', $condition, "The correct condition is returned.");
-  }
-
 }
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php
index 35f891378f87..3586df971a94 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\Entity;
 
 use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Form\FormState;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -94,4 +95,32 @@ public function providerTestFormIds() {
     );
   }
 
+  /**
+   * @covers ::copyFormValuesToEntity
+   */
+  public function testCopyFormValuesToEntity() {
+    $entity_id = 'test_config_entity_id';
+    $values = ['id' => $entity_id];
+    $entity = $this->getMockBuilder('\Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginCollections')
+      ->setConstructorArgs([$values, 'test_config_entity'])
+      ->setMethods(['getPluginCollections'])
+      ->getMock();
+    $entity->expects($this->atLeastOnce())
+      ->method('getPluginCollections')
+      ->willReturn(['key_controlled_by_plugin_collection' => NULL]);
+    $this->entityForm->setEntity($entity);
+
+    $form_state = (new FormState())->setValues([
+      'regular_key' => 'foo',
+      'key_controlled_by_plugin_collection' => 'bar',
+    ]);
+    $result = $this->entityForm->buildEntity([], $form_state);
+
+    $this->assertSame($entity_id, $result->id());
+    // The regular key should have a value, but the one controlled by a plugin
+    // collection should not have been set.
+    $this->assertSame('foo', $result->get('regular_key'));
+    $this->assertNull($result->get('key_controlled_by_plugin_collection'));
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
index 0122f495741d..b586c0250695 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
@@ -243,6 +243,9 @@ public function testApplyContextMapping() {
     );
 
     $plugin = $this->getMock('Drupal\Component\Plugin\ContextAwarePluginInterface');
+    $plugin->expects($this->once())
+      ->method('getContextMapping')
+      ->willReturn([]);
     $plugin->expects($this->once())
       ->method('getContextDefinitions')
       ->will($this->returnValue(array('hit' => 'hit')));
@@ -266,6 +269,9 @@ public function testApplyContextMappingConfigurable() {
     );
 
     $plugin = $this->getMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
+    $plugin->expects($this->once())
+      ->method('getContextMapping')
+      ->willReturn([]);
     $plugin->expects($this->once())
       ->method('getContextDefinitions')
       ->will($this->returnValue(array('hit' => 'hit')));
@@ -289,6 +295,9 @@ public function testApplyContextMappingConfigurableAssigned() {
     );
 
     $plugin = $this->getMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
+    $plugin->expects($this->once())
+      ->method('getContextMapping')
+      ->willReturn([]);
     $plugin->expects($this->once())
       ->method('getContextDefinitions')
       ->will($this->returnValue(array('hit' => 'hit')));
@@ -315,6 +324,9 @@ public function testApplyContextMappingConfigurableAssignedMiss() {
     );
 
     $plugin = $this->getMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
+    $plugin->expects($this->once())
+      ->method('getContextMapping')
+      ->willReturn([]);
     $plugin->expects($this->once())
       ->method('getContextDefinitions')
       ->will($this->returnValue(array('hit' => 'hit')));
-- 
GitLab