diff --git a/core/modules/filter/src/Element/ProcessedText.php b/core/modules/filter/src/Element/ProcessedText.php
index 2f92b22..750a6fc 100644
--- a/core/modules/filter/src/Element/ProcessedText.php
+++ b/core/modules/filter/src/Element/ProcessedText.php
@@ -8,13 +8,13 @@
 namespace Drupal\filter\Element;
 
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\Core\Render\Element\RenderElement;
 use Drupal\Core\Render\Renderer;
 use Drupal\filter\Entity\FilterFormat;
 use Drupal\filter\Plugin\FilterInterface;
+use Drupal\filter\Render\FilteredString;
 
 /**
  * Provides a processed text render element.
@@ -124,7 +124,7 @@ public static function preRenderText($element) {
     // safe, but it has been passed through the filter system and checked with
     // a text format, so it must be printed as is. (See the note about security
     // in the method documentation above.)
-    $element['#markup'] = SafeMarkup::set($text);
+    $element['#markup'] = FilteredString::create($text);
 
     // Set the updated bubbleable rendering metadata and the text format's
     // cache tag.
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index ed241d8..14f3873 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -13,6 +13,7 @@
 use Drupal\Component\Utility\Xss;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Render\FilteredString;
 
 /**
  * Provides a filter to caption elements.
@@ -59,7 +60,10 @@ public function process($text, $langcode) {
         $node->removeAttribute('class');
         $filter_caption = array(
           '#theme' => 'filter_caption',
-          '#node' => SafeMarkup::set($node->C14N()),
+          // We pass the unsanitized string because this is a text format
+          // filter, and after filtering, we always assume the output is safe.
+          // @see \Drupal\filter\Element\ProcessedText::preRenderText()
+          '#node' => FilteredString::create($node->C14N()),
           '#tag' => $node->tagName,
           '#caption' => $caption,
           '#classes' => $classes,
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 80ceb75..d0d9761 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -1246,7 +1246,6 @@ public function renderText($alter) {
       $value = strip_tags($value, $alter['preserve_tags']);
     }
 
-    $suffix = '';
     if (!empty($alter['trim']) && !empty($alter['max_length'])) {
       $length = strlen($value);
       $value = $this->renderTrimText($alter, $value);
@@ -1267,8 +1266,6 @@ public function renderText($alter) {
         // @todo Views should expect and store a leading /. See
         //   https://www.drupal.org/node/2423913.
         $more_link = \Drupal::l($more_link_text, CoreUrl::fromUserInput('/' . $more_link_path, array('attributes' => array('class' => array('views-more-link')))));
-
-        $suffix .= " " . $more_link;
       }
     }
 
@@ -1283,8 +1280,10 @@ public function renderText($alter) {
       }
       $value = $this->renderAsLink($alter, $value, $tokens);
     }
-
-    return $value . $suffix;
+    if (isset($more_link)) {
+      $value .= " " . $more_link;
+    }
+    return $value;
   }
 
   /**
