diff --git a/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php b/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php
index 74e5ef7..776c918 100644
--- a/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/DialogRenderer.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Render\MainContent;
 
+use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\OpenDialogCommand;
@@ -58,7 +59,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
     $options = $request->request->get('dialogOptions', array());
     $target = $this->determineTargetSelector($options, $route_match);
 
-    $response->addCommand(new OpenDialogCommand($target, $title, $content, $options));
+    $response->addCommand(new OpenDialogCommand($target, PlainTextOutput::renderFromHtml($title), $content, $options));
     return $response;
   }
 
diff --git a/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php b/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php
index 8f2ef17..727c64f 100644
--- a/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/ModalRenderer.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Render\MainContent;
 
+use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\OpenModalDialogCommand;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -38,7 +39,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
     // otherwise get it from the routing information.
     $options = $request->request->get('dialogOptions', array());
 
-    $response->addCommand(new OpenModalDialogCommand($title, $content, $options));
+    $response->addCommand(new OpenModalDialogCommand(PlainTextOutput::renderFromHtml($title), $content, $options));
     return $response;
   }
 
diff --git a/core/modules/system/src/Tests/Ajax/DialogTest.php b/core/modules/system/src/Tests/Ajax/DialogTest.php
index 333cc6a..e3399ca 100644
--- a/core/modules/system/src/Tests/Ajax/DialogTest.php
+++ b/core/modules/system/src/Tests/Ajax/DialogTest.php
@@ -43,7 +43,7 @@ public function testDialog() {
       'data' => $dialog_contents,
       'dialogOptions' => array(
         'modal' => TRUE,
-        'title' => 'AJAX Dialog contents',
+        'title' => 'AJAX Dialog & contents',
       ),
     );
     $form_expected_response = array(
@@ -71,7 +71,7 @@ public function testDialog() {
       'data' => $dialog_contents,
       'dialogOptions' => array(
         'modal' => FALSE,
-        'title' => 'AJAX Dialog contents',
+        'title' => 'AJAX Dialog & contents',
       ),
     );
     $no_target_expected_response = array(
@@ -81,7 +81,7 @@ public function testDialog() {
       'data' => $dialog_contents,
       'dialogOptions' => array(
         'modal' => FALSE,
-        'title' => 'AJAX Dialog contents',
+        'title' => 'AJAX Dialog & contents',
       ),
     );
     $close_expected_response = array(
@@ -101,6 +101,9 @@ public function testDialog() {
     // Emulate going to the JS version of the page and check the JSON response.
     $ajax_result = $this->drupalGetAjax('ajax-test/dialog-contents', array('query' => array(MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_modal')));
     $this->assertEqual($modal_expected_response, $ajax_result[3], 'Modal dialog JSON response matches.');
+    // Test the HTML escaping of & character.
+    $this->assertEqual($ajax_result[3]['dialogOptions']['title'], 'AJAX Dialog & contents');
+    $this->assertNotEqual($ajax_result[3]['dialogOptions']['title'], 'AJAX Dialog &amp; contents');
 
     // Check that requesting a "normal" dialog without JS goes to a page.
     $this->drupalGet('ajax-test/dialog-contents');
@@ -156,6 +159,8 @@ public function testDialog() {
 
     // Check that the response matches the expected value.
     $this->assertEqual($modal_expected_response, $ajax_result[4], 'POST request modal dialog JSON response matches.');
+    // Test the HTML escaping of & character.
+    $this->assertNotEqual($ajax_result[4]['dialogOptions']['title'], 'AJAX Dialog &amp; contents');
 
     // Abbreviated test for "normal" dialogs, testing only the difference.
     $ajax_result = $this->drupalPostAjaxForm('ajax-test/dialog', array(), 'button2');
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index ff4859d..35df374 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -28,7 +28,7 @@ class AjaxTestController {
   public static function dialogContents() {
     // This is a regular render array; the keys do not have special meaning.
     $content = array(
-      '#title' => 'AJAX Dialog contents',
+      '#title' => '<em>AJAX Dialog & contents</em>',
       'content' => array(
         '#markup' => 'Example message',
       ),
diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
index 922ef60d..a82e6da 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
@@ -98,7 +98,7 @@ public function nonModal(&$form, FormStateInterface $form_state) {
   protected function dialog($is_modal = FALSE) {
     $content = AjaxTestController::dialogContents();
     $response = new AjaxResponse();
-    $title = $this->t('AJAX Dialog contents');
+    $title = $this->t('AJAX Dialog & contents');
 
     // Attach the library necessary for using the Open(Modal)DialogCommand and
     // set the attachments for this Ajax response.
diff --git a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
index b3edb64..acf8e0d 100644
--- a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
+++ b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
@@ -246,7 +246,7 @@ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state)
         'width' => '75%',
       );
 
-      $response->addCommand(new OpenModalDialogCommand($title, $display, $options));
+      $response->addCommand(new OpenModalDialogCommand(Html::decodeEntities(strip_tags($title)), $display, $options));
 
       if ($section = $form_state->get('#section')) {
         $response->addCommand(new Ajax\HighlightCommand('.' . Html::cleanCssIdentifier($section)));
diff --git a/core/modules/views_ui/src/Tests/FieldUITest.php b/core/modules/views_ui/src/Tests/FieldUITest.php
index b6018f7..577b0c9 100644
--- a/core/modules/views_ui/src/Tests/FieldUITest.php
+++ b/core/modules/views_ui/src/Tests/FieldUITest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views_ui\Tests;
 
+use Drupal\Component\Serialization\Json;
 use Drupal\views\Views;
 
 /**
@@ -60,6 +61,24 @@ public function testFieldUI() {
 
     $result = $this->xpath('//details[@id="edit-options-more"]');
     $this->assertEqual(empty($result), true, "Container 'more' is empty and should not be displayed.");
+
+    // Ensure that dialog titles are not escaped.
+    $edit_groupby_url = 'admin/structure/views/nojs/handler/test_view/default/field/name';
+    $this->assertNoLinkByHref($edit_groupby_url, 0, 'No aggregation link found.');
+
+    // Enable aggregation on the view.
+    $edit = array(
+      'group_by' => TRUE,
+    );
+    $this->drupalPostForm('/admin/structure/views/nojs/display/test_view/default/group_by', $edit, t('Apply'));
+
+    $this->assertLinkByHref($edit_groupby_url, 0, 'Aggregation link found.');
+
+    $edit_handler_url = '/admin/structure/views/ajax/handler-group/test_view/default/field/name';
+    $this->drupalGet($edit_handler_url);
+    $data = Json::decode($this->getRawContent());
+    $this->assertNotEqual($data[3]['dialogOptions']['title'], 'Configure aggregation settings for field <em class="placeholder">Views test: Name</em>');
+    $this->assertEqual($data[3]['dialogOptions']['title'], 'Configure aggregation settings for field Views test: Name');
   }
 
   /**
