diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 6d558fee6cd00d04bce0381fea95fcea195dc2b8..25fb2749dd145debab6975482849fe8940989a88 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Extension\Extension;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -82,6 +83,38 @@ function filter_theme() {
   );
 }
 
+/**
+ * Implements hook_system_info_alter().
+ *
+ * Prevents uninstallation of modules that provide filter plugins that are being
+ * used in a filter format.
+ */
+function filter_system_info_alter(&$info, Extension $file, $type) {
+  // It is not safe to call filter_formats() during maintenance mode.
+  if ($type == 'module' && !defined('MAINTENANCE_MODE')) {
+    // Get filter plugins supplied by this module.
+    $filter_plugins = array_filter(\Drupal::service('plugin.manager.filter')->getDefinitions(), function ($definition) use ($file) {
+      return $definition['provider'] == $file->getName();
+    });
+    if (!empty($filter_plugins)) {
+      $used_in = [];
+      // Find out if any filter formats have the plugin enabled.
+      foreach (filter_formats() as $filter_format) {
+        foreach ($filter_plugins as $filter_plugin) {
+          if ($filter_format->filters($filter_plugin['id'])->status) {
+            $used_in[] = $filter_format->label();
+            $info['required'] = TRUE;
+            break;
+          }
+        }
+      }
+      if (!empty($used_in)) {
+        $info['explanation'] = t('Provides a filter plugin that is in use in the following filter formats: %formats', array('%formats' => implode(', ', $used_in)));
+      }
+    }
+  }
+}
+
 /**
  * Retrieves a list of enabled text formats, ordered by weight.
  *
diff --git a/core/modules/filter/src/Entity/FilterFormat.php b/core/modules/filter/src/Entity/FilterFormat.php
index 4b00af187788ee8a6851681a85cf61fcd3f8f4c5..0b7c6afee95e545bca6250c973e383d32042ef78 100644
--- a/core/modules/filter/src/Entity/FilterFormat.php
+++ b/core/modules/filter/src/Entity/FilterFormat.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Entity;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -387,4 +388,44 @@ public function getHtmlRestrictions() {
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function removeFilter($instance_id) {
+    unset($this->filters[$instance_id]);
+    $this->filterCollection->removeInstanceId($instance_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onDependencyRemoval(array $dependencies) {
+    $changed = FALSE;
+    $filters = $this->filters();
+    foreach ($filters as $filter) {
+      // Remove disabled filters, so that this FilterFormat config entity can
+      // continue to exist.
+      if (!$filter->status && in_array($filter->provider, $dependencies['module'])) {
+        $this->removeFilter($filter->getPluginId());
+        $changed = TRUE;
+      }
+    }
+    if ($changed) {
+      $this->save();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function calculatePluginDependencies(PluginInspectionInterface $instance) {
+    // Only add dependencies for plugins that are actually configured. This is
+    // necessary because the filter plugin collection will return all available
+    // filter plugins.
+    // @see \Drupal\filter\FilterPluginCollection::getConfiguration()
+    if (isset($this->filters[$instance->getPluginId()])) {
+      parent::calculatePluginDependencies($instance);
+    }
+  }
+
 }
diff --git a/core/modules/filter/src/FilterFormatInterface.php b/core/modules/filter/src/FilterFormatInterface.php
index b5e75ce78b928c3086ec9a807166784ff16d0aa3..b9c558b9738be0e2d675007e6afa38250bfe8166 100644
--- a/core/modules/filter/src/FilterFormatInterface.php
+++ b/core/modules/filter/src/FilterFormatInterface.php
@@ -84,4 +84,12 @@ public function getFilterTypes();
    */
   public function getHtmlRestrictions();
 
+  /**
+   * Removes a filter.
+   *
+   * @param string $instance_id
+   *   The ID of a filter plugin to be removed.
+   */
+  public function removeFilter($instance_id);
+
 }
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 588546e147fdfd134efb648d1c137ce16c068b9d..2808fc169f6b06d8d554455ec073a477279876ce 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\filter\Tests;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\Session\AnonymousUserSession;
 use Drupal\Core\TypedData\OptionsProviderInterface;
 use Drupal\Core\TypedData\DataDefinition;
@@ -386,4 +387,68 @@ public function assertFilterFormatViolation(ConstraintViolationListInterface $vi
     }
     $this->assertTrue($filter_format_violation_found, format_string('Validation violation for invalid value "%invalid_value" found', array('%invalid_value' => $invalid_value)));
   }
+
+  /**
+   * Tests that filter format dependency removal works.
+   *
+   * Ensure that modules providing filter plugins are required when the plugin
+   * is in use, and that only disabled plugins are removed from format
+   * configuration entities rather than the configuration entities being
+   * deleted.
+   *
+   * @see \Drupal\filter\Entity\FilterFormat::onDependencyRemoval()
+   * @see filter_system_info_alter()
+   */
+  public function testDependencyRemoval() {
+    $this->installSchema('user', array('users_data'));
+    $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html');
+
+    // Enable the filter_test_restrict_tags_and_attributes filter plugin on the
+    // filtered_html filter format.
+    $filter_config = [
+      'weight' => 10,
+      'status' => 1,
+    ];
+    $filter_format->setFilterConfig('filter_test_restrict_tags_and_attributes', $filter_config)->save();
+
+    $module_data = _system_rebuild_module_data();
+    $this->assertTrue($module_data['filter_test']->info['required'], 'The filter_test module is required.');
+    $this->assertEqual($module_data['filter_test']->info['explanation'], String::format('Provides a filter plugin that is in use in the following filter formats: %formats', array('%formats' => $filter_format->label())));
+
+    // Disable the filter_test_restrict_tags_and_attributes filter plugin but
+    // have custom configuration so that the filter plugin is still configured
+    // in filtered_html the filter format.
+    $filter_config = [
+      'weight' => 20,
+      'status' => 0,
+    ];
+    $filter_format->setFilterConfig('filter_test_restrict_tags_and_attributes', $filter_config)->save();
+    // Use the get method to match the assert after the module has been
+    // uninstalled.
+    $filters = $filter_format->get('filters');
+    $this->assertTrue(isset($filters['filter_test_restrict_tags_and_attributes']), 'The filter plugin filter_test_restrict_tags_and_attributes is configured by the filtered_html filter format.');
+
+    drupal_static_reset('filter_formats');
+    \Drupal::entityManager()->getStorage('filter_format')->resetCache();
+    $module_data = _system_rebuild_module_data();
+    $this->assertFalse(isset($module_data['filter_test']->info['required']), 'The filter_test module is required.');
+
+    // Verify that a dependency exists on the module that provides the filter
+    // plugin since it has configuration for the disabled plugin.
+    $this->assertEqual(['module' => ['filter_test']], $filter_format->getDependencies());
+
+    // Uninstall the module.
+    \Drupal::service('module_installer')->uninstall(array('filter_test'));
+
+    // Verify the filter format still exists but the dependency and filter is
+    // gone.
+    \Drupal::entityManager()->getStorage('filter_format')->resetCache();
+    $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html');
+    $this->assertEqual([], $filter_format->getDependencies());
+    // Use the get method since the FilterFormat::filters() method only returns
+    // existing plugins.
+    $filters = $filter_format->get('filters');
+    $this->assertFalse(isset($filters['filter_test_restrict_tags_and_attributes']), 'The filter plugin filter_test_restrict_tags_and_attributes is not configured by the filtered_html filter format.');
+  }
+
 }