diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
index 03f37e1..29691e8 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
@@ -23,6 +23,7 @@
  * @Editor(
  *   id = "ckeditor",
  *   label = @Translation("CKEditor"),
+ *   supports_content_filtering = TRUE,
  *   supports_inline_editing = TRUE
  * )
  */
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
index 039ee24..e97c7e7 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
@@ -103,6 +103,7 @@ function testLoading() {
       'format' => 'filtered_html',
       'editor' => 'ckeditor',
       'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
+      'editorSupportsContentFiltering' => TRUE,
     )));
     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
     $this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
@@ -129,6 +130,7 @@ function testLoading() {
       'format' => 'filtered_html',
       'editor' => 'ckeditor',
       'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
+      'editorSupportsContentFiltering' => TRUE,
     )));
     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
     $this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index affe711..9e05d99 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -93,6 +93,7 @@ function editor_library_info() {
       array('system', 'drupal'),
       array('system', 'drupalSettings'),
       array('system', 'jquery.once'),
+      array('system', 'drupal.dialog'),
     ),
   );
 
diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js
index c1cdcbc..62733f4 100644
--- a/core/modules/editor/js/editor.js
+++ b/core/modules/editor/js/editor.js
@@ -3,11 +3,118 @@
  * Attaches behavior for the Editor module.
  */
 
-(function ($, Drupal) {
+(function ($, Drupal, drupalSettings) {
 
 "use strict";
 
 /**
+ * Finds the text area field associated with the given text format selector.
+ *
+ * @param jQuery $formatSelector
+ *   A text format selector DOM element.
+ *
+ * @return DOM
+ *   The text area DOM element.
+ */
+function findFieldForFormatSelector ($formatSelector) {
+  var field_id = $formatSelector.attr('data-editor-for');
+  return $('#' + field_id).get(0);
+}
+
+/**
+ * Changes the text editor on the text area for the given text format selector.
+ *
+ * @param jQuery $formatSelector
+ *   A text format selector DOM element.
+ * @param String activeFormatID
+ *   The currently active text format; its associated text editor will be
+ *   detached.
+ * @param String newFormatID
+ *   The text format we're changing to; its associated text editor will be
+ *   attached.
+ */
+function changeTextEditor ($formatSelector, activeFormatID, newFormatID) {
+  var field = findFieldForFormatSelector($formatSelector);
+  // Detach the current editor (if any) and attach a new editor.
+  if (drupalSettings.editor.formats[activeFormatID]) {
+    Drupal.editorDetach(field, drupalSettings.editor.formats[activeFormatID]);
+  }
+  activeFormatID = newFormatID;
+  if (drupalSettings.editor.formats[activeFormatID]) {
+    Drupal.editorAttach(field, drupalSettings.editor.formats[activeFormatID]);
+  }
+  $formatSelector.attr('data-editor-active-text-format', newFormatID);
+}
+
+/**
+ * Handles changes in text format.
+ *
+ * @param jQuery.Event event
+ */
+function onTextFormatChange (event) {
+  var $select = $(event.target);
+  var activeFormatID = $select.attr('data-editor-active-text-format');
+  var newFormatID = $select.val();
+
+  // Prevent double-attaching if the change event is triggered manually.
+  if (newFormatID === activeFormatID) {
+    return;
+  }
+
+  // When changing to a text format that has a text editor associated
+  // with it that supports content filtering, then first ask for
+  // confirmation, because switching text formats might cause certain
+  // markup to be stripped away.
+  if (drupalSettings.editor.formats[newFormatID] && drupalSettings.editor.formats[newFormatID].editorSupportsContentFiltering) {
+    var message = Drupal.t('Changing the text format to %text_format will permanently remove content that is not allowed in that text format.<br><br>Save your changes before switching the text format to avoid losing data.', {
+      '%text_format': $select.find('option:selected').text()
+    });
+    var confirmationDialog = Drupal.dialog('<div>' + message + '</div>', {
+      title: Drupal.t('Change text format?'),
+      dialogClass: 'editor-change-text-format-modal',
+      resizable: false,
+      buttons: [
+        {
+          text: Drupal.t('Continue'),
+          'class': 'button button--primary',
+          click: function () {
+            changeTextEditor($select, activeFormatID, newFormatID);
+            confirmationDialog.close();
+          }
+        },
+        {
+          text: Drupal.t('Cancel'),
+          'class': 'button',
+          click: function () {
+            // Restore the active format ID: cancel changing text format. We cannot
+            // simply call event.preventDefault() because jQuery's change event is
+            // only triggered after the change has already been accepted.
+            $select.val(activeFormatID);
+            confirmationDialog.close();
+          }
+        }
+      ],
+      // Prevent this modal from being closed without the user making a choice
+      // as per http://stackoverflow.com/a/5438771.
+      closeOnEscape: false,
+      create: function () {
+        $(this).parent().find('.ui-dialog-titlebar-close').remove();
+      },
+      beforeClose: false,
+      close: function (event) {
+        // Automatically destroy the DOM element that was used for the dialog.
+        $(event.target).remove();
+      }
+    });
+
+    confirmationDialog.showModal();
+  }
+  else {
+    changeTextEditor($select, activeFormatID, newFormatID);
+  }
+}
+
+/**
  * Initialize an empty object for editors to place their attachment code.
  */
 Drupal.editors = {};
@@ -22,12 +129,11 @@ Drupal.behaviors.editor = {
       return;
     }
 
-    var $context = $(context);
-    var behavior = this;
-    $context.find('.editor').once('editor', function () {
+    $(context).find('.editor').once('editor', function () {
       var $this = $(this);
       var activeFormatID = $this.val();
-      var field = behavior.findFieldForFormatSelector($this);
+      $this.attr('data-editor-active-text-format', activeFormatID);
+      var field = findFieldForFormatSelector($this);
 
       // Directly attach this editor, if the text format is enabled.
       if (settings.editor.formats[activeFormatID]) {
@@ -36,23 +142,7 @@ Drupal.behaviors.editor = {
 
       // Attach onChange handler to text format selector element.
       if ($this.is('select')) {
-        $this.on('change.editorAttach', function () {
-          var newFormatID = $this.val();
-
-          // Prevent double-attaching if the change event is triggered manually.
-          if (newFormatID === activeFormatID) {
-            return;
-          }
-
-          // Detach the current editor (if any) and attach a new editor.
-          if (settings.editor.formats[activeFormatID]) {
-            Drupal.editorDetach(field, settings.editor.formats[activeFormatID]);
-          }
-          activeFormatID = newFormatID;
-          if (settings.editor.formats[activeFormatID]) {
-            Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
-          }
-        });
+        $this.on('change.editorAttach', onTextFormatChange);
       }
       // Detach any editor when the containing form is submitted.
       $this.parents('form').on('submit', function (event) {
@@ -81,20 +171,14 @@ Drupal.behaviors.editor = {
       editors = $(context).find('.editor').removeOnce('editor');
     }
 
-    var behavior = this;
     editors.each(function () {
       var $this = $(this);
       var activeFormatID = $this.val();
-      var field = behavior.findFieldForFormatSelector($this);
+      var field = findFieldForFormatSelector($this);
       if (activeFormatID in settings.editor.formats) {
         Drupal.editorDetach(field, settings.editor.formats[activeFormatID], trigger);
       }
     });
-  },
-
-  findFieldForFormatSelector: function ($formatSelector) {
-    var field_id = $formatSelector.attr('data-editor-for');
-    return $('#' + field_id).get(0);
   }
 };
 
@@ -133,4 +217,4 @@ Drupal.editorDetach = function (field, format, trigger) {
   }
 };
 
-})(jQuery, Drupal);
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php b/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
index fd9b049..be126b4 100644
--- a/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
+++ b/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
@@ -33,6 +33,13 @@ class Editor extends Plugin {
   public $label;
 
   /**
+   * Whether the editor supports "allowed content only" filtering.
+   *
+   * @var boolean
+   */
+  public $supports_content_filtering;
+
+  /**
    * Whether the editor supports the inline editing provided by the Edit module.
    *
    * @var boolean
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/EditorBase.php b/core/modules/editor/lib/Drupal/editor/Plugin/EditorBase.php
index 5610d33..20a4169 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/EditorBase.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/EditorBase.php
@@ -24,6 +24,8 @@
  * - id: The unique, system-wide identifier of the text editor. Typically named
  *   the same as the editor library.
  * - label: The human-readable name of the text editor, translated.
+ * - supports_content_filtering: Whether the editor supports "allowed content
+ *   only" filtering.
  * - supports_inline_editing: Whether the editor supports the inline editing
  *   provided by the Edit module.
  *
@@ -33,6 +35,7 @@
  * @Editor(
  *   id = "myeditor",
  *   label = @Translation("My Editor"),
+ *   supports_content_filtering = FALSE,
  *   supports_inline_editing = FALSE
  * )
  * @endcode
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php b/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
index fcc2076..558cfde 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/EditorManager.php
@@ -72,6 +72,7 @@ public function getAttachments(array $format_ids) {
       }
 
       $plugin = $this->createInstance($editor->editor);
+      $plugin_definition = $plugin->getPluginDefinition();
 
       // Libraries.
       $attachments['library'] = array_merge($attachments['library'], $plugin->getLibraries($editor));
@@ -81,6 +82,7 @@ public function getAttachments(array $format_ids) {
         'format' => $format_id,
         'editor' => $editor->editor,
         'editorSettings' => $plugin->getJSSettings($editor),
+        'editorSupportsContentFiltering' => $plugin_definition['supports_content_filtering'],
       );
     }
 
diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditorLoadingTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditorLoadingTest.php
index 640530e..ffbf9a8 100644
--- a/core/modules/editor/lib/Drupal/editor/Tests/EditorLoadingTest.php
+++ b/core/modules/editor/lib/Drupal/editor/Tests/EditorLoadingTest.php
@@ -96,6 +96,7 @@ public function testLoading() {
       'format' => 'full_html',
       'editor' => 'unicorn',
       'editorSettings' => array('ponyModeEnabled' => TRUE),
+      'editorSupportsContentFiltering' => TRUE,
     )));
     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
     $this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
@@ -123,6 +124,7 @@ public function testLoading() {
       'format' => 'plain_text',
       'editor' => 'unicorn',
       'editorSettings' => array('ponyModeEnabled' => TRUE),
+      'editorSupportsContentFiltering' => TRUE,
     )));
     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
     $this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php
index 5f2ac0a..78140c7 100644
--- a/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php
+++ b/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php
@@ -104,6 +104,7 @@ public function testManager() {
               'format'  => 'full_html',
               'editor' => 'unicorn',
               'editorSettings' => $unicorn_plugin->getJSSettings($editor),
+              'editorSupportsContentFiltering' => TRUE,
             )
           )))
         )
diff --git a/core/modules/editor/tests/modules/lib/Drupal/editor_test/Plugin/Editor/UnicornEditor.php b/core/modules/editor/tests/modules/lib/Drupal/editor_test/Plugin/Editor/UnicornEditor.php
index 8944cee..e6b34cd 100644
--- a/core/modules/editor/tests/modules/lib/Drupal/editor_test/Plugin/Editor/UnicornEditor.php
+++ b/core/modules/editor/tests/modules/lib/Drupal/editor_test/Plugin/Editor/UnicornEditor.php
@@ -16,6 +16,7 @@
  * @Editor(
  *   id = "unicorn",
  *   label = @Translation("Unicorn Editor"),
+ *   supports_content_filtering = TRUE,
  *   supports_inline_editing = TRUE
  * )
  */