diff --git a/core/core.services.yml b/core/core.services.yml
index 3e2feb6f0b0fba4fe1c2551d5fdcd7f8e4433c29..98ddc9ec13f2608e6d35474d6b2e06104a8e50a9 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -289,7 +289,14 @@ services:
     arguments: ['@app.root', '%container.modules%', '@cache.bootstrap']
   module_installer:
     class: Drupal\Core\Extension\ModuleInstaller
+    tags:
+      - { name: service_collector, tag: 'module_install.uninstall_validator', call: addUninstallValidator }
     arguments: ['@app.root', '@module_handler', '@kernel']
+  content_uninstall_validator:
+    class: Drupal\Core\Entity\ContentUninstallValidator
+    tags:
+      - { name: module_install.uninstall_validator }
+    arguments: ['@entity.manager', '@string_translation']
   theme_handler:
     class: Drupal\Core\Extension\ThemeHandler
     arguments: ['@app.root', '@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder_indicator']
diff --git a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..cc139e02293f7df4a74fffb134c8181682b904ab
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Entity\ContentUninstallValidator.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Validates module uninstall readiness based on existing content entities.
+ */
+class ContentUninstallValidator implements ModuleUninstallValidatorInterface {
+  use StringTranslationTrait;
+
+  /**
+   * Constructs a new ContentUninstallValidator.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
+    $this->entityManager = $entity_manager;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($module) {
+    $entity_types = $this->entityManager->getDefinitions();
+    $reasons = array();
+    foreach ($entity_types as $entity_type) {
+      if ($module == $entity_type->getProvider() && $entity_type instanceof ContentEntityTypeInterface && $this->entityManager->getStorage($entity_type->id())->hasData()) {
+        $reasons[] = $this->t('There is content for the entity type: @entity_type', array('@entity_type' => $entity_type->getLabel()));
+      }
+    }
+    return $reasons;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
index c77dd340c4f29c7c09ae5cee3c4f94e58f48d6d3..c6f392185c694d6cb747e4ce53935fa237f4d5c5 100644
--- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php
+++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
@@ -41,6 +41,13 @@ class ModuleInstaller implements ModuleInstallerInterface {
    */
   protected $root;
 
+  /**
+   * The uninstall validators.
+   *
+   * @var \Drupal\Core\Extension\ModuleUninstallValidatorInterface[]
+   */
+  protected $uninstallValidators;
+
   /**
    * Constructs a new ModuleInstaller instance.
    *
@@ -60,6 +67,13 @@ public function __construct($root, ModuleHandlerInterface $module_handler, Drupa
     $this->kernel = $kernel;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function addUninstallValidator(ModuleUninstallValidatorInterface $uninstall_validator) {
+    $this->uninstallValidators[] = $uninstall_validator;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -306,6 +320,15 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
       }
     }
 
+    // Use the validators and throw an exception with the reasons.
+    if ($reasons = $this->validateUninstall($module_list)) {
+      foreach ($reasons as $reason) {
+        $reason_message[] = implode(', ', $reason);
+      }
+      throw new ModuleUninstallValidatorException(format_string('The following reasons prevents the modules from being uninstalled: @reasons', array(
+        '@reasons' => implode(', ', $reason_message),
+      )));
+    }
     // Set the actual module weights.
     $module_list = array_map(function ($module) use ($module_data) {
       return $module_data[$module]->sort;
@@ -469,4 +492,23 @@ protected function updateKernel($module_filenames) {
     $this->moduleHandler = $container->get('module_handler');
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function validateUninstall(array $module_list) {
+    $reasons = array();
+    foreach ($module_list as $module) {
+      foreach ($this->uninstallValidators as $validator) {
+        $validation_reasons = $validator->validate($module);
+        if (!empty($validation_reasons)) {
+          if (!isset($reasons[$module])) {
+            $reasons[$module] = array();
+          }
+          $reasons[$module] = array_merge($reasons[$module], $validation_reasons);
+        }
+      }
+    }
+    return $reasons;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php
index 246af4b1cffbc0b3cae3708e4d63135e06f79ad9..95cb25e0878e13c736121f4dad6da8194e23117b 100644
--- a/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php
+++ b/core/lib/Drupal/Core/Extension/ModuleInstallerInterface.php
@@ -23,7 +23,7 @@ interface ModuleInstallerInterface {
    *   - Invoke hook_install() and add it to the list of installed modules.
    * - Invoke hook_modules_installed().
    *
-   * @param array $module_list
+   * @param string[] $module_list
    *   An array of module names.
    * @param bool $enable_dependencies
    *   (optional) If TRUE, dependencies will automatically be installed in the
@@ -42,7 +42,7 @@ public function install(array $module_list, $enable_dependencies = TRUE);
   /**
    * Uninstalls a given list of modules.
    *
-   * @param array $module_list
+   * @param string[] $module_list
    *   The modules to uninstall.
    * @param bool $uninstall_dependents
    *   (optional) If TRUE, dependent modules will automatically be uninstalled
@@ -58,5 +58,24 @@ public function install(array $module_list, $enable_dependencies = TRUE);
    */
   public function uninstall(array $module_list, $uninstall_dependents = TRUE);
 
+  /**
+   * Adds module a uninstall validator.
+   *
+   * @param \Drupal\Core\Extension\ModuleUninstallValidatorInterface $uninstall_validator
+   *   The uninstall validator to add.
+   */
+  public function addUninstallValidator(ModuleUninstallValidatorInterface $uninstall_validator);
+
+  /**
+   * Determines whether a list of modules can be uninstalled.
+   *
+   * @param string[] $module_list
+   *   An array of module names.
+   *
+   * @return string[]
+   *   An array of reasons the module can not be uninstalled, empty if it can.
+   */
+  public function validateUninstall(array $module_list);
+
 }
 
diff --git a/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorException.php b/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorException.php
new file mode 100644
index 0000000000000000000000000000000000000000..c28240e38d8031515dd918e09a09a04746c0ba57
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorException.php
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\ModuleUninstallValidatorException.
+ */
+
+namespace Drupal\Core\Extension;
+
+/**
+ * Defines an exception thrown when uninstalling a module that did not validate.
+ */
+class ModuleUninstallValidatorException extends \InvalidArgumentException { }
diff --git a/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorInterface.php b/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e62b0ef7fd8d12904bf40c58e27e46486e2c0d0
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ModuleUninstallValidatorInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Extension\ModuleUninstallValidatorInterface.
+ */
+
+namespace Drupal\Core\Extension;
+
+/**
+ * Common interface for module uninstall validators.
+ */
+interface ModuleUninstallValidatorInterface {
+
+  /**
+   * Determines the reasons a module can not be uninstalled.
+   *
+   * @param string $module
+   *   A module name.
+   *
+   * @return string[]
+   *   An array of reasons the module can not be uninstalled, empty if it can.
+   */
+  public function validate($module);
+}
diff --git a/core/modules/config/src/Tests/ConfigImportAllTest.php b/core/modules/config/src/Tests/ConfigImportAllTest.php
index b620ad8a46e18843aade74b87565a306791d1692..a7ce00de24f55f0a14c74e11908439d5d1b8687a 100644
--- a/core/modules/config/src/Tests/ConfigImportAllTest.php
+++ b/core/modules/config/src/Tests/ConfigImportAllTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Config\StorageComparer;
 use Drupal\system\Tests\Module\ModuleTestBase;
+use Drupal\shortcut\Entity\Shortcut;
 
 /**
  * Tests the largest configuration import possible with the modules and profiles
@@ -83,6 +84,10 @@ public function testInstallUninstall() {
       $term->delete();
     }
 
+    // Delete any shortcuts so the shortcut module can be uninstalled.
+    $shortcuts = Shortcut::loadMultiple();
+    entity_delete_multiple('shortcut', array_keys($shortcuts));
+
     system_list_reset();
     $all_modules = system_rebuild_module_data();
 
diff --git a/core/modules/system/src/Form/ModulesUninstallForm.php b/core/modules/system/src/Form/ModulesUninstallForm.php
index 4285956e7b7bdef91e043607d4791a54a44bf15e..153e2caf8eb7e62732f3db9adfe3d3bf9e15007d 100644
--- a/core/modules/system/src/Form/ModulesUninstallForm.php
+++ b/core/modules/system/src/Form/ModulesUninstallForm.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Form;
 
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Extension\ModuleInstallerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
@@ -25,6 +26,13 @@ class ModulesUninstallForm extends FormBase {
    */
   protected $moduleHandler;
 
+  /**
+   * The module installer service.
+   *
+   * @var \Drupal\Core\Extension\ModuleInstallerInterface
+   */
+  protected $moduleInstaller;
+
   /**
    * The expirable key value store.
    *
@@ -38,6 +46,7 @@ class ModulesUninstallForm extends FormBase {
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('module_handler'),
+      $container->get('module_installer'),
       $container->get('keyvalue.expirable')->get('modules_uninstall')
     );
   }
@@ -47,11 +56,14 @@ public static function create(ContainerInterface $container) {
    *
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
+   *   The module installer.
    * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable
    *   The key value expirable factory.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, KeyValueStoreExpirableInterface $key_value_expirable) {
+  public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable) {
     $this->moduleHandler = $module_handler;
+    $this->moduleInstaller = $module_installer;
     $this->keyValueExpirable = $key_value_expirable;
   }
 
@@ -69,7 +81,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     // Make sure the install API is available.
     include_once DRUPAL_ROOT . '/core/includes/install.inc';
 
-    // Get a list of disabled, installed modules.
+    // Get a list of all available modules.
     $modules = system_rebuild_module_data();
     $uninstallable = array_filter($modules, function ($module) use ($modules) {
       return empty($modules[$module->getName()]->info['required']) && drupal_get_installed_schema_version($module->getName()) > SCHEMA_UNINSTALLED;
@@ -110,9 +122,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Sort all modules by their name.
     uasort($uninstallable, 'system_sort_modules_by_info_name');
+    $validation_reasons = $this->moduleInstaller->validateUninstall(array_keys($uninstallable));
 
     $form['uninstall'] = array('#tree' => TRUE);
-    foreach ($uninstallable as $module) {
+    foreach ($uninstallable as $module_key => $module) {
       $name = $module->info['name'] ?: $module->getName();
       $form['modules'][$module->getName()]['#module_name'] = $name;
       $form['modules'][$module->getName()]['name']['#markup'] = $name;
@@ -124,6 +137,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         '#title_display' => 'invisible',
       );
 
+      // If a validator returns reasons not to uninstall a module,
+      // list the reasons and disable the check box.
+      if (isset($validation_reasons[$module_key])) {
+        $form['modules'][$module->getName()]['#validation_reasons'] = $validation_reasons[$module_key];
+        $form['uninstall'][$module->getName()]['#disabled'] = TRUE;
+      }
       // All modules which depend on this one must be uninstalled first, before
       // we can allow this module to be uninstalled. (The installation profile
       // is excluded from this list.)
diff --git a/core/modules/system/src/Tests/Extension/ModuleHandlerTest.php b/core/modules/system/src/Tests/Extension/ModuleHandlerTest.php
index 83cd5fc5c0236914a9f4fa757ddf7d9a30f4019c..48fbf0e1dd73942e2a6d204e7c3a1a090a352f3c 100644
--- a/core/modules/system/src/Tests/Extension/ModuleHandlerTest.php
+++ b/core/modules/system/src/Tests/Extension/ModuleHandlerTest.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\simpletest\KernelTestBase;
-use Symfony\Component\HttpFoundation\Response;
+use \Drupal\Core\Extension\ModuleUninstallValidatorException;
 
 /**
  * Tests ModuleHandler functionality.
@@ -199,6 +199,60 @@ function testUninstallProfileDependency() {
     $this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.');
   }
 
+  /**
+   * Tests uninstalling a module that has content.
+   */
+  function testUninstallContentDependency() {
+    $this->enableModules(array('module_test', 'entity_test', 'text', 'user', 'help'));
+    $this->assertTrue($this->moduleHandler()->moduleExists('entity_test'), 'Test module is enabled.');
+    $this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.');
+
+    $this->installSchema('user', 'users_data');
+    $entity_types = \Drupal::entityManager()->getDefinitions();
+    foreach ($entity_types as $entity_type) {
+      if ('entity_test' == $entity_type->getProvider()) {
+        $this->installEntitySchema($entity_type->id());
+      }
+    }
+
+    // Create a fake dependency.
+    // entity_test will depend on help. This way help can not be uninstalled
+    // when there is test content preventing entity_test from being uninstalled.
+    \Drupal::state()->set('module_test.dependency', 'dependency');
+    drupal_static_reset('system_rebuild_module_data');
+
+    // Create an entity so that the modules can not be disabled.
+    $entity = entity_create('entity_test', array('name' => $this->randomString()));
+    $entity->save();
+
+    // Uninstalling entity_test is not possible when there is content.
+    try {
+      $message = 'ModuleHandler::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
+      $this->moduleInstaller()->uninstall(array('entity_test'));
+      $this->fail($message);
+    }
+    catch (ModuleUninstallValidatorException $e) {
+      $this->pass(get_class($e) . ': ' . $e->getMessage());
+    }
+
+    // Uninstalling help needs entity_test to be un-installable.
+    try {
+      $message = 'ModuleHandler::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
+      $this->moduleInstaller()->uninstall(array('help'));
+      $this->fail($message);
+    }
+    catch (ModuleUninstallValidatorException $e) {
+      $this->pass(get_class($e) . ': ' . $e->getMessage());
+    }
+
+    // Deleting the entity.
+    $entity->delete();
+
+    $result = $this->moduleInstaller()->uninstall(array('help'));
+    $this->assertTrue($result, 'ModuleHandler::uninstall() returns TRUE.');
+    $this->assertEqual(drupal_get_installed_schema_version('entity_test'), SCHEMA_UNINSTALLED, "entity_test module was uninstalled.");
+  }
+
   /**
    * Tests whether the correct module metadata is returned.
    */
diff --git a/core/modules/system/src/Tests/Module/UninstallTest.php b/core/modules/system/src/Tests/Module/UninstallTest.php
index f48a8aa428ae74b64e64a2ffb49ebab32a7375ca..aa06bda51d1b49f354dde72f57c51b486a01cb17 100644
--- a/core/modules/system/src/Tests/Module/UninstallTest.php
+++ b/core/modules/system/src/Tests/Module/UninstallTest.php
@@ -43,9 +43,22 @@ function testUserPermsUninstalled() {
   function testUninstallPage() {
     $account = $this->drupalCreateUser(array('administer modules'));
     $this->drupalLogin($account);
+
+    // Create a node type.
+    $node_type = entity_create('node_type', array('type' => 'uninstall_blocker'));
+    $node_type->save();
+    // Add a node to prevent node from being uninstalled.
+    $node = entity_create('node', array('type' => 'uninstall_blocker'));
+    $node->save();
+
     $this->drupalGet('admin/modules/uninstall');
     $this->assertTitle(t('Uninstall') . ' | Drupal');
 
+    $this->assertText(\Drupal::translation()->translate('The following reasons prevents Node from being uninstalled: There is content for the entity type: Content'), 'Content prevents uninstalling node module.');
+    // Delete the node to allow node to be uninstalled.
+    $node->delete();
+    $node_type->delete();
+
     // Uninstall module_test.
     $edit = array();
     $edit['uninstall[module_test]'] = TRUE;
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2ef1ea237a152bc5723755ae0629b007d4c30856..197767f6f65eda0c4abd8a0d54f9dd52e8b1b6a0 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -303,14 +303,16 @@ function theme_system_modules_uninstall($variables) {
   // Display table.
   $rows = array();
   foreach (Element::children($form['modules']) as $module) {
+    $disabled_message = '';
+    // Add the modules requiring the module in question as a validation reason.
     if (!empty($form['modules'][$module]['#required_by'])) {
-      $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
-        'To uninstall @module, the following module must be uninstalled first: @required_modules',
-        'To uninstall @module, the following modules must be uninstalled first: @required_modules',
-        array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
+      $form['modules'][$module]['#validation_reasons'][] = \Drupal::translation()->translate('Required by: @modules', array('@modules' => implode(', ',$form['modules'][$module]['#required_by'])));
     }
-    else {
-      $disabled_message = '';
+    if (!empty($form['modules'][$module]['#validation_reasons'])) {
+      $disabled_message = \Drupal::translation()->formatPlural(count($form['modules'][$module]['#validation_reasons']),
+        'The following reason prevents @module from being uninstalled: @reasons',
+        'The following reasons prevents @module from being uninstalled: @reasons',
+        array('@module' => $form['modules'][$module]['#module_name'], '@reasons' => implode('; ', $form['modules'][$module]['#validation_reasons'])));
     }
     $rows[] = array(
       array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
diff --git a/core/modules/system/tests/modules/module_test/module_test.module b/core/modules/system/tests/modules/module_test/module_test.module
index 60d53ef3909544842611df304d2dd69381af75a1..92f038b3dfb4c6fd0d7fcc7eae32e374a88351dd 100644
--- a/core/modules/system/tests/modules/module_test/module_test.module
+++ b/core/modules/system/tests/modules/module_test/module_test.module
@@ -27,6 +27,10 @@ function module_test_system_info_alter(&$info, Extension $file, $type) {
       // Make config module depend on help module.
       $info['dependencies'][] = 'help';
     }
+    elseif ($file->getName() == 'entity_test') {
+      // Make entity test module depend on help module.
+      $info['dependencies'][] = 'help';
+    }
   }
   elseif (\Drupal::state()->get('module_test.dependency') == 'version dependency') {
     if ($file->getName() == 'color') {