 core/modules/filter/src/Element/ProcessedText.php  |   7 +-
 core/modules/filter/src/FilterProcessResult.php    |   2 +-
 .../modules/hal/tests/src/Kernel/NormalizeTest.php |  26 ++++-
 .../Comment/CommentResourceTestBase.php            |  16 +++
 .../EntityResource/EntityResourceTestBase.php      |  15 +++
 .../EntityTestTextItemNormalizerTest.php           |  81 +++++++++++++++
 .../EntityResource/Term/TermResourceTestBase.php   |  19 +++-
 .../field_normalization_test.services.yml          |   4 +-
 .../tests/src/Kernel/EntitySerializationTest.php   |  35 ++++++-
 .../src/Plugin/Field/FieldType/TextItemBase.php    |  12 ++-
 core/modules/text/src/TextProcessed.php            |  53 ++--------
 core/modules/text/src/TextProcessedResult.php      | 109 +++++++++++++++++++++
 .../tests/src/Kernel/TextWithSummaryItemTest.php   |   2 +
 13 files changed, 324 insertions(+), 57 deletions(-)

diff --git a/core/modules/filter/src/Element/ProcessedText.php b/core/modules/filter/src/Element/ProcessedText.php
index 5c7a212..e3ba8fd 100644
--- a/core/modules/filter/src/Element/ProcessedText.php
+++ b/core/modules/filter/src/Element/ProcessedText.php
@@ -3,6 +3,7 @@
 namespace Drupal\filter\Element;
 
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\Core\Render\Element\RenderElement;
 use Drupal\filter\Entity\FilterFormat;
@@ -69,7 +70,11 @@ public static function preRenderText($element) {
     $langcode = $element['#langcode'];
 
     if (!isset($format_id)) {
-      $format_id = static::configFactory()->get('filter.settings')->get('fallback_format');
+      $filter_settings = static::configFactory()->get('filter.settings');
+      $format_id = $filter_settings->get('fallback_format');
+      CacheableMetadata::createFromRenderArray($element)
+        ->addCacheableDependency($filter_settings)
+        ->applyTo($element);
     }
     /** @var \Drupal\filter\Entity\FilterFormat $format **/
     $format = FilterFormat::load($format_id);
diff --git a/core/modules/filter/src/FilterProcessResult.php b/core/modules/filter/src/FilterProcessResult.php
index 3fa592b..2a8a71b 100644
--- a/core/modules/filter/src/FilterProcessResult.php
+++ b/core/modules/filter/src/FilterProcessResult.php
@@ -78,7 +78,7 @@ class FilterProcessResult extends BubbleableMetadata {
    * @param string $processed_text
    *   The text as processed by a text filter.
    */
-  public function __construct($processed_text) {
+  public function __construct($processed_text = '') {
     $this->processedText = $processed_text;
   }
 
diff --git a/core/modules/hal/tests/src/Kernel/NormalizeTest.php b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
index d837b79..e77d4b4 100644
--- a/core/modules/hal/tests/src/Kernel/NormalizeTest.php
+++ b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Url;
 use Drupal\entity_test\Entity\EntityTest;
+use Drupal\filter\Entity\FilterFormat;
 
 /**
  * Tests HAL normalization edge cases for EntityResource.
@@ -18,6 +19,28 @@ class NormalizeTest extends NormalizerTestBase {
    */
   protected function setUp() {
     parent::setUp();
+    // Create a text format because it is needed for TextItemBase normalization.
+    // @see \Drupal\text\Normalizer\TextItemBaseNormalizer::normalize().
+    FilterFormat::create([
+      'format' => 'my_text_format',
+      'name' => 'My Text Format',
+      'filters' => [
+        'filter_html' => [
+          'module' => 'filter',
+          'status' => TRUE,
+          'weight' => 10,
+          'settings' => [
+            'allowed_html' => '<p>',
+          ],
+        ],
+        'filter_autop' => [
+          'module' => 'filter',
+          'status' => TRUE,
+          'weight' => 10,
+          'settings' => [],
+        ],
+      ],
+    ])->save();
 
     \Drupal::service('router.builder')->rebuild();
   }
@@ -37,7 +60,7 @@ public function testNormalize() {
       'name' => $this->randomMachineName(),
       'field_test_text' => [
         'value' => $this->randomMachineName(),
-        'format' => 'full_html',
+        'format' => 'my_text_format',
       ],
       'field_test_entity_reference' => [
         'target_id' => $target_entity_de->id(),
@@ -152,6 +175,7 @@ public function testNormalize() {
         [
           'value' => $values['field_test_text']['value'],
           'format' => $values['field_test_text']['format'],
+          'process_result' => "<p>{$values['field_test_text']['value']}</p>",
         ],
       ],
     ];
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
index bade2a7..afb3e04 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
@@ -5,6 +5,7 @@
 use Drupal\comment\Entity\Comment;
 use Drupal\comment\Entity\CommentType;
 use Drupal\comment\Tests\CommentTestTrait;
+use Drupal\Core\Cache\Cache;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
 use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
@@ -197,6 +198,7 @@ protected function getExpectedNormalizedEntity() {
         [
           'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
           'format' => 'plain_text',
+          'process_result' => '<p>The name &quot;llama&quot; was adopted by European settlers from native Peruvians.</p>' . "\n",
         ],
       ],
     ];
@@ -249,6 +251,20 @@ protected function getNormalizedPatchEntity() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheTags() {
+    return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheContexts() {
+    return Cache::mergeContexts(['languages:language_interface', 'theme'], parent::getExpectedCacheContexts());
+  }
+
+  /**
    * Tests POSTing a comment without critical base fields.
    *
    * testPost() is testing with the most minimal normalization possible: the one
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 6faf028..8a0a5e9 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -1365,4 +1365,19 @@ protected function assertResourceNotAvailable(Url $url, array $request_options)
     }
   }
 
+  /**
+   * Sorts a nested array with ksort().
+   *
+   * @param $array
+   *   The nested array to sort.
+   */
+  public static function nestedKsort(&$array) {
+    ksort($array);
+    foreach ($array as &$item) {
+      if (is_array($item)) {
+        static::nestedKsort($item);
+      }
+    }
+  }
+
 }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
new file mode 100644
index 0000000..c563b5c
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class EntityTestTextItemNormalizerTest extends EntityTestResourceTestBase {
+
+  use AnonResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $format = 'json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $mimeType = 'application/json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedNormalizedEntity() {
+    $expected = parent::getExpectedNormalizedEntity();
+    $expected['field_test_text'] = [
+      [
+        'value' => 'Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.',
+        'format' => 'plain_text',
+        'process_result' => '<p>Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.</p>' . "\n",
+      ],
+    ];
+    return $expected;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function createEntity() {
+    $entity = parent::createEntity();
+    $entity->field_test_text = [
+      'value' => 'Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.',
+      'format' => 'plain_text',
+    ];
+    $entity->save();
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNormalizedPostEntity() {
+    $post_entity = parent::getNormalizedPostEntity();
+    $post_entity['field_test_text'] = [
+      [
+        'value' => 'Llamas are awesome.',
+        'format' => 'plain_text',
+      ],
+    ];
+    return $post_entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheTags() {
+    return Cache::mergeTags(['config:filter.format.plain_text'], parent::getExpectedCacheTags());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheContexts() {
+    return Cache::mergeContexts(['languages:language_interface', 'theme'], parent::getExpectedCacheContexts());
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
index e0a1581..f3750a2 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\rest\Functional\EntityResource\Term;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\taxonomy\Entity\Term;
 use Drupal\taxonomy\Entity\Vocabulary;
 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
@@ -79,6 +80,7 @@ protected function createEntity() {
     // Create a "Llama" taxonomy term.
     $term = Term::create(['vid' => $vocabulary->id()])
       ->setName('Llama')
+      ->setDescription("It is a little known fact that llamas cannot count higher the seven.")
       ->setChangedTime(123456789)
       ->set('path', '/llama');
     $term->save();
@@ -109,8 +111,9 @@ protected function getExpectedNormalizedEntity() {
       ],
       'description' => [
         [
-          'value' => NULL,
+          'value' => 'It is a little known fact that llamas cannot count higher the seven.',
           'format' => NULL,
+          'process_result' => "<p>It is a little known fact that llamas cannot count higher the seven.</p>\n",
         ],
       ],
       'parent' => [],
@@ -230,4 +233,18 @@ public function testPatchPath() {
     $this->assertSame($normalization['path'], $updated_normalization['path']);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheTags() {
+    return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text', 'config:filter.settings']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheContexts() {
+    return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
+  }
+
 }
diff --git a/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml
index 36243e7..51fdd4a 100644
--- a/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml
+++ b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml
@@ -2,5 +2,5 @@ services:
   serializer.normalizer.silly_fielditem:
     class: Drupal\field_normalization_test\Normalization\TextItemSillyNormalizer
     tags:
-      # The priority must be higher than serialization.normalizer.field_item.
-      - { name: normalizer , priority: 9 }
+      # The priority must be higher than serializer.normalizer.text_item_base.
+      - { name: normalizer , priority: 30 }
diff --git a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
index 5b71b93..5012847 100644
--- a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
+++ b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\Tests\serialization\Kernel;
 
+use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\entity_test\Entity\EntityTestMulRev;
+use Drupal\filter\Entity\FilterFormat;
 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
 
 /**
@@ -63,6 +65,29 @@ protected function setUp() {
     // User create needs sequence table.
     $this->installSchema('system', ['sequences']);
 
+    // Create a text format because it is needed for TextItemBase normalization.
+    // @see \Drupal\text\Normalizer\TextItemBaseNormalizer::normalize().
+    FilterFormat::create([
+      'format' => 'my_text_format',
+      'name' => 'My Text Format',
+      'filters' => [
+        'filter_html' => [
+          'module' => 'filter',
+          'status' => TRUE,
+          'weight' => 10,
+          'settings' => [
+            'allowed_html' => '<p>',
+          ],
+        ],
+        'filter_autop' => [
+          'module' => 'filter',
+          'status' => TRUE,
+          'weight' => 10,
+          'settings' => [],
+        ],
+      ],
+    ])->save();
+
     // Create a test user to use as the entity owner.
     $this->user = \Drupal::entityManager()->getStorage('user')->create([
       'name' => 'serialization_test_user',
@@ -72,12 +97,13 @@ protected function setUp() {
     $this->user->save();
 
     // Create a test entity to serialize.
+    $test_text_value = $this->randomMachineName();
     $this->values = [
       'name' => $this->randomMachineName(),
       'user_id' => $this->user->id(),
       'field_test_text' => [
-        'value' => $this->randomMachineName(),
-        'format' => 'full_html',
+        'value' => $test_text_value,
+        'format' => 'my_text_format',
       ],
     ];
     $this->entity = EntityTestMulRev::create($this->values);
@@ -134,6 +160,7 @@ public function testNormalize() {
         [
           'value' => $this->values['field_test_text']['value'],
           'format' => $this->values['field_test_text']['format'],
+          'process_result' => "<p>{$this->values['field_test_text']['value']}</p>",
         ],
       ],
     ];
@@ -174,7 +201,7 @@ public function testSerialize() {
     // JsonEncoder. The output of ComplexDataNormalizer::normalize() is tested
     // elsewhere, so we can just assume that it works properly here.
     $normalized = $this->serializer->normalize($this->entity, 'json');
-    $expected = json_encode($normalized);
+    $expected = Json::encode($normalized);
     // Test 'json'.
     $actual = $this->serializer->serialize($this->entity, 'json');
     $this->assertIdentical($actual, $expected, 'Entity serializes to JSON when "json" is requested.');
@@ -202,7 +229,7 @@ public function testSerialize() {
       'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
       'revision_translation_affected' => '<revision_translation_affected><value>1</value></revision_translation_affected>',
       'non_rev_field' => '<non_rev_field/>',
-      'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
+      'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format><process_result><![CDATA[<p>' . $this->values['field_test_text']['value'] . '</p>]]></process_result></field_test_text>',
     ];
     // Sort it in the same order as normalised.
     $expected = array_merge($normalized, $expected);
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php b/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
index 6dd4339..bee0d9c 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
@@ -7,6 +7,8 @@
 use Drupal\Core\Field\FieldItemBase;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
+use Drupal\text\TextProcessed;
+use Drupal\text\TextProcessedResult;
 
 /**
  * Base class for 'text' configurable field types.
@@ -31,6 +33,14 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
       ->setClass('\Drupal\text\TextProcessed')
       ->setSetting('text source', 'value');
 
+    $properties['process_result'] = DataDefinition::create('string')
+      ->setLabel(t('Processed text (object)'))
+      ->setDescription(t('The text with the text format applied.'))
+      ->setComputed(TRUE)
+      ->setClass(TextProcessedResult::class)
+      ->setSetting('text source', 'value')
+      ->setInternal(FALSE);
+
     return $properties;
   }
 
@@ -57,7 +67,7 @@ public function isEmpty() {
   public function onChange($property_name, $notify = TRUE) {
     // Unset processed properties that are affected by the change.
     foreach ($this->definition->getPropertyDefinitions() as $property => $definition) {
-      if ($definition->getClass() == '\Drupal\text\TextProcessed') {
+      if (in_array($definition->getClass(), [TextProcessed::class, TextProcessedResult::class], TRUE)) {
         if ($property_name == 'format' || ($definition->getSetting('text source') == $property_name)) {
           $this->writePropertyValue($property, NULL);
         }
diff --git a/core/modules/text/src/TextProcessed.php b/core/modules/text/src/TextProcessed.php
index 61bef37..dd9c185 100644
--- a/core/modules/text/src/TextProcessed.php
+++ b/core/modules/text/src/TextProcessed.php
@@ -2,9 +2,8 @@
 
 namespace Drupal\text;
 
-use Drupal\Core\TypedData\DataDefinitionInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\Render\Markup;
+use Drupal\filter\FilterProcessResult;
 
 /**
  * A computed property for processing text with a format.
@@ -12,56 +11,18 @@
  * Required settings (below the definition's 'settings' key) are:
  *  - text source: The text property containing the to be processed text.
  */
-class TextProcessed extends TypedData {
-
-  /**
-   * Cached processed text.
-   *
-   * @var string|null
-   */
-  protected $processed = NULL;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
-    parent::__construct($definition, $name, $parent);
-
-    if ($definition->getSetting('text source') === NULL) {
-      throw new \InvalidArgumentException("The definition's 'text source' key has to specify the name of the text property to be processed.");
-    }
-  }
+class TextProcessed extends TextProcessedResult {
 
   /**
    * {@inheritdoc}
    */
   public function getValue() {
-    if ($this->processed !== NULL) {
-      return $this->processed;
-    }
-
-    $item = $this->getParent();
-    $text = $item->{($this->definition->getSetting('text source'))};
+    $value = parent::getValue();
 
-    // Avoid running check_markup() on empty strings.
-    if (!isset($text) || $text === '') {
-      $this->processed = '';
-    }
-    else {
-      $this->processed = check_markup($text, $item->format, $item->getLangcode());
-    }
-    return $this->processed;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setValue($value, $notify = TRUE) {
-    $this->processed = $value;
-    // Notify the parent of any changes.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
+    if ($value !== '' || ($value instanceof FilterProcessResult && $value->getProcessedText() !== '')) {
+      $value = Markup::create((string) $value);
     }
+    return $value;
   }
 
 }
diff --git a/core/modules/text/src/TextProcessedResult.php b/core/modules/text/src/TextProcessedResult.php
new file mode 100644
index 0000000..5486d11
--- /dev/null
+++ b/core/modules/text/src/TextProcessedResult.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Drupal\text;
+
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
+use Drupal\filter\FilterProcessResult;
+
+/**
+ * A computed property for processing text with a format.
+ *
+ * Required settings (below the definition's 'settings' key) are:
+ *  - text source: The text property containing the to be processed text.
+ *
+ * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
+ *  This functionality will be moved into \Drupal\text\TextProcessed
+ */
+class TextProcessedResult extends TypedData implements CacheableDependencyInterface {
+
+  /**
+   * Cached processed text.
+   *
+   * @var \Drupal\filter\FilterProcessResult|null
+   */
+  protected $processed = NULL;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
+    parent::__construct($definition, $name, $parent);
+
+    if ($definition->getSetting('text source') === NULL) {
+      throw new \InvalidArgumentException("The definition's 'text source' key has to specify the name of the text property to be processed.");
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    if ($this->processed !== NULL) {
+      return $this->processed->getProcessedText();
+    }
+
+    /** @var \Drupal\Core\Field\FieldItemInterface $item */
+    $item = $this->getParent();
+    $text = $item->{($this->definition->getSetting('text source'))};
+
+    // Avoid running \Drupal\Core\Render\RendererInterface::renderPlain
+    // on empty strings.
+    if (!isset($text) || $text === '') {
+      $this->processed = new FilterProcessResult('');
+    }
+    else {
+      $build = [
+        '#type' => 'processed_text',
+        '#text' => $text,
+        '#format' => $item->format,
+        '#filter_types_to_skip' => [],
+        '#langcode' => $item->getLangcode(),
+      ];
+      // It's necessary to capture the cacheability metadata associated with the
+      // processed text. See https://www.drupal.org/node/2278483.
+      $processed_text = $this->getRenderer()->renderPlain($build);
+      $this->processed = FilterProcessResult::createFromRenderArray($build)->setProcessedText((string) $processed_text);
+    }
+    return $this->processed->getProcessedText();
+  }
+
+  public function getCacheTags() {
+    $this->getValue();
+    return $this->processed->getCacheTags();
+  }
+
+
+  public function getCacheContexts() {
+    $this->getValue();
+    return $this->processed->getCacheContexts();
+  }
+
+  public function getCacheMaxAge() {
+    $this->getValue();
+    return $this->processed->getCacheMaxAge();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    $this->processed = $value;
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * Returns the renderer service.
+   *
+   * @return \Drupal\Core\Render\RendererInterface
+   */
+  protected function getRenderer() {
+    return \Drupal::service('renderer');
+  }
+
+}
diff --git a/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
index a48c191..fa9db40 100644
--- a/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
+++ b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
@@ -81,6 +81,8 @@ public function testCrudAndUpdate() {
 
     // Change the format, this should update the processed properties.
     $entity->summary_field->format = 'no_filters';
+    $entity->save();
+    $entity = $storage->load($entity->id());
     $this->assertEqual($entity->summary_field->processed, $value);
     $this->assertEqual($entity->summary_field->summary_processed, $summary);
 
