 core/core.services.yml                             |  4 +++-
 core/lib/Drupal/Core/Entity/EntityViewBuilder.php  |  1 -
 core/lib/Drupal/Core/Render/Renderer.php           | 24 +++++++++++++++++++---
 core/modules/block/src/BlockViewBuilder.php        |  1 -
 .../src/Tests/Entity/EntityCacheTagsTestBase.php   |  7 ++++---
 .../src/Tests/Entity/EntityViewBuilderTest.php     |  7 ++++---
 .../Tests/Core/Render/RendererBubblingTest.php     |  4 ++++
 .../Core/Render/RendererPostRenderCacheTest.php    | 11 ++++++++++
 .../Drupal/Tests/Core/Render/RendererTest.php      |  4 +++-
 .../Drupal/Tests/Core/Render/RendererTestBase.php  | 11 +++++++++-
 sites/default/default.services.yml                 |  8 ++++++++
 11 files changed, 68 insertions(+), 14 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index 6c209ce..865b6a4 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1,6 +1,8 @@
 parameters:
   session.storage.options: {}
   twig.config: {}
+  renderer.config:
+    default_cache_contexts: ['theme']
   factory.keyvalue:
     default: keyvalue.database
   factory.keyvalue.expirable:
@@ -1307,6 +1309,6 @@ services:
       - { name: mime_type_guesser }
   renderer:
     class: Drupal\Core\Render\Renderer
-    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts']
+    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts', '%renderer.config%']
   email.validator:
     class: Egulias\EmailValidator\EmailValidator
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 071de3e..d12d028 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -170,7 +170,6 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       '#cache' => array(
         'tags' => Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags()),
         'contexts' => [
-          'theme',
           'user.roles',
         ],
       ),
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index 06f9c4f..7a556d1 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -65,6 +65,13 @@ class Renderer implements RendererInterface {
   protected $cacheContexts;
 
   /**
+   * The renderer configuration array.
+   *
+   * @var array
+   */
+  protected $rendererConfig;
+
+  /**
    * The stack containing bubbleable rendering metadata.
    *
    * @var \SplStack|null
@@ -86,14 +93,17 @@ class Renderer implements RendererInterface {
    *   The cache factory.
    * @param \Drupal\Core\Cache\CacheContexts $cache_contexts
    *   The cache contexts service.
+   * @param array $renderer_config
+   *   The renderer configuration array.
    */
-  public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContexts $cache_contexts) {
+  public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContexts $cache_contexts, array $renderer_config) {
     $this->controllerResolver = $controller_resolver;
     $this->theme = $theme;
     $this->elementInfo = $element_info;
     $this->requestStack = $request_stack;
     $this->cacheFactory = $cache_factory;
     $this->cacheContexts = $cache_contexts;
+    $this->rendererConfig = $renderer_config;
   }
 
   /**
@@ -164,10 +174,19 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
     static::$stack->push(new BubbleableMetadata());
 
+    // Bubbleable rendering metadata that has configurable defaults.
+    $default_cache_contexts = $this->rendererConfig['default_cache_contexts'];
+    if (isset($elements['#cache']['contexts'])) {
+      $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $default_cache_contexts);
+    }
+    else {
+      $elements['#cache']['contexts'] = $default_cache_contexts;
+    }
+
     // Try to fetch the prerendered element from cache, run any
     // #post_render_cache callbacks and return the final markup.
     $pre_bubbling_cid = NULL;
-    if (isset($elements['#cache'])) {
+    if (isset($elements['#cache']['keys']) || isset($elements['#cache']['cid'])) {
       $cached_element = $this->cacheGet($elements);
       if ($cached_element !== FALSE) {
         $elements = $cached_element;
@@ -216,7 +235,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
     }
 
     // Defaults for bubbleable rendering metadata.
-    $elements['#cache']['contexts'] = isset($elements['#cache']['contexts']) ? $elements['#cache']['contexts'] : array();
     $elements['#cache']['tags'] = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array();
     $elements['#cache']['max-age'] = isset($elements['#cache']['max-age']) ? $elements['#cache']['max-age'] : Cache::PERMANENT;
     $elements['#attached'] = isset($elements['#attached']) ? $elements['#attached'] : array();
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index d527d6d..fd30318 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -88,7 +88,6 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
         );
         $default_cache_contexts = array(
           'languages',
-          'theme',
         );
         $max_age = $plugin->getCacheMaxAge();
         $build[$entity_id]['#cache'] += array(
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 3b382e1..3a072b1 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -315,7 +315,8 @@ public function testReferencedEntity() {
     $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]);
 
     // The default cache contexts for rendered entities.
-    $entity_cache_contexts = ['theme', 'user.roles'];
+    $default_cache_contexts = ['theme'];
+    $entity_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['user.roles']);
 
     // Cache tags present on every rendered page.
     $page_cache_tags = Cache::mergeTags(
@@ -395,7 +396,7 @@ public function testReferencedEntity() {
     $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
     // Verify the entity type's list cache contexts are present.
     $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
-    $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
+    $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
 
 
     $this->pass("Test listing containing referenced entity.", 'Debug');
@@ -405,7 +406,7 @@ public function testReferencedEntity() {
     $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
     // Verify the entity type's list cache contexts are present.
     $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
-    $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
+    $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
 
 
     // Verify that after modifying the referenced entity, there is a cache miss
diff --git a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
index eba7809..5b62f27 100644
--- a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\user\Entity\Role;
 use Drupal\user\RoleInterface;
 
@@ -58,7 +59,7 @@ public function testEntityViewBuilderCache() {
     // Get a fully built entity view render array.
     $entity_test->save();
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['theme'])));
     $cid = implode(':', $cid_parts);
     $bin = $build['#cache']['bin'];
 
@@ -108,7 +109,7 @@ public function testEntityViewBuilderCacheWithReferences() {
 
     // Get a fully built entity view render array for the referenced entity.
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['theme'])));
     $cid_reference = implode(':', $cid_parts);
     $bin_reference = $build['#cache']['bin'];
 
@@ -127,7 +128,7 @@ public function testEntityViewBuilderCacheWithReferences() {
 
     // Get a fully built entity view render array.
     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
-    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
+    $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys(Cache::mergeContexts($build['#cache']['contexts'], ['theme'])));
     $cid = implode(':', $cid_parts);
     $bin = $build['#cache']['bin'];
 
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index 929a307..d9ca898 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -21,6 +21,10 @@ class RendererBubblingTest extends RendererTestBase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    // Disable the default cache contexts, so that this test can test just the
+    // bubbling behavior.
+    $this->rendererConfig['default_cache_contexts'] = [];
+
     parent::setUp();
 
     $this->setUpRequest();
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
index da9f232..3d0224e 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php
@@ -17,6 +17,17 @@
 class RendererPostRenderCacheTest extends RendererTestBase {
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    // Disable the default cache contexts, so that this test can test just the
+    // #post_render_cache behavior.
+    $this->rendererConfig['default_cache_contexts'] = [];
+
+    parent::setUp();
+  }
+
+  /**
    * Generates an element with a #post_render_cache callback.
    *
    * @return array
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index a53155f..03c385e 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -19,7 +19,9 @@ class RendererTest extends RendererTestBase {
 
   protected $defaultThemeVars = [
     '#cache' => [
-      'contexts' => [],
+      'contexts' => [
+        'theme',
+      ],
       'tags' => [],
       'max-age' => Cache::PERMANENT,
     ],
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index b4320c9..7b2bac2 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -70,6 +70,15 @@ class RendererTestBase extends UnitTestCase {
   protected $memoryCache;
 
   /**
+   * The mocked renderer configuration.
+   *
+   * @var array
+   */
+  protected $rendererConfig = [
+    'default_cache_contexts' => ['theme'],
+  ];
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -83,7 +92,7 @@ protected function setUp() {
     $this->cacheContexts = $this->getMockBuilder('Drupal\Core\Cache\CacheContexts')
       ->disableOriginalConstructor()
       ->getMock();
-    $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->requestStack, $this->cacheFactory, $this->cacheContexts);
+    $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->requestStack, $this->cacheFactory, $this->cacheContexts, $this->rendererConfig);
 
     $container = new ContainerBuilder();
     $container->set('renderer', $this->renderer);
diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml
index 77e99cc..9e4d19c 100644
--- a/sites/default/default.services.yml
+++ b/sites/default/default.services.yml
@@ -76,6 +76,14 @@ parameters:
     # Not recommended in production environments
     # @default true
     cache: true
+  renderer.config:
+    # Renderer default cache contexts:
+    #
+    # The Renderer will automatically associate these cache contexts with every
+    # render array, hence varying every render array by these cache contexts.
+    #
+    # @default ['theme']
+    default_cache_contexts: ['theme']
   factory.keyvalue:
     {}
     # Default key/value storage service to use.
