diff --git a/core/core.services.yml b/core/core.services.yml
index 183f7e2..8a5092e 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -160,7 +160,12 @@ services:
     arguments: [default]
   form_builder:
     class: Drupal\Core\Form\FormBuilder
-    arguments: ['@form_validator', '@form_submitter', '@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@request_stack', '@class_resolver', '@theme.manager', '@?csrf_token', '@?http_kernel']
+    arguments: ['@form_validator', '@form_submitter', '@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@request_stack', '@class_resolver', '@?csrf_token', '@?http_kernel']
+  form_builder.alter:
+    class: Drupal\Core\Form\FormAlterSubscriber
+    arguments: ['@module_handler', '@theme.manager']
+    tags:
+      - { name: event_subscriber }
   form_validator:
     class: Drupal\Core\Form\FormValidator
     arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form']
diff --git a/core/lib/Drupal/Core/Form/FormAlterEvent.php b/core/lib/Drupal/Core/Form/FormAlterEvent.php
new file mode 100644
index 0000000..d973461
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormAlterEvent.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormAlterEvent.
+ */
+
+namespace Drupal\Core\Form;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Wraps a form and its current state for altering.
+ */
+class FormAlterEvent extends Event {
+
+  /**
+   * An associative array containing the structure of the form.
+   *
+   * @var array
+   */
+  protected $form;
+
+  /**
+   * The current state of the form.
+   *
+   * @var \Drupal\Core\Form\FormStateInterface
+   */
+  protected $formState;
+
+  /**
+   * The alter hooks to be invoked.
+   *
+   * @var array
+   */
+  protected $hooks;
+
+  /**
+   * The unique string identifying the form.
+   *
+   * @var string
+   */
+  protected $formId;
+
+  /**
+   * Constructs a new FormAlterEvent.
+   *
+   * @param array $hooks
+   *   An array of the alter hooks to invoke.
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param string $form_id
+   *   The unique string identifying the desired form.
+   */
+  function __construct($hooks, &$form, FormStateInterface $form_state, $form_id) {
+    $this->hooks = $hooks;
+    $this->form = &$form;
+    $this->formState = $form_state;
+    $this->formId = $form_id;
+  }
+
+  /**
+   * Returns the form.
+   *
+   * @return array
+   *   An associative array containing the structure of the form.
+   */
+  public function &getForm() {
+    return $this->form;
+  }
+
+  /**
+   * Returns the form ID.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormId() {
+    return $this->formId;
+  }
+
+  /**
+   * Returns the form state.
+   *
+   * @return \Drupal\Core\Form\FormStateInterface
+   *   The current state of the form.
+   */
+  public function getFormState() {
+    return $this->formState;
+  }
+
+  /**
+   * Returns the array of alter hooks.
+   *
+   * @return array
+   *   An array of the alter hooks to invoke.
+   */
+  public function getHooks() {
+    return $this->hooks;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Form/FormAlterSubscriber.php b/core/lib/Drupal/Core/Form/FormAlterSubscriber.php
new file mode 100644
index 0000000..1fd1bf8
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormAlterSubscriber.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormAlterSubscriber.
+ */
+
+namespace Drupal\Core\Form;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Allows the module handler and theme manager to alter forms.
+ */
+class FormAlterSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface
+   */
+  protected $themeManager;
+
+  /**
+   * Constructs a new FormAlterSubscriber.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
+   *   The theme manager.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) {
+    $this->moduleHandler = $module_handler;
+    $this->themeManager = $theme_manager;
+  }
+
+  /**
+   * Allows the module handler and theme manager to alter forms.
+   */
+  public function onFormAlter(FormAlterEvent $event) {
+    $hooks = $event->getHooks();
+    $form = &$event->getForm();
+    $form_state = $event->getFormState();
+    $form_id = $event->getFormId();
+
+    $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
+    $this->themeManager->alter($hooks, $form, $form_state, $form_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[FormEvents::ALTER][] = 'onFormAlter';
+    return $events;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 6c473f2..82f0787 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -18,7 +18,6 @@
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Site\Settings;
-use Drupal\Core\Theme\ThemeManagerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Response;
@@ -91,13 +90,6 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
   protected $currentUser;
 
   /**
-   * The theme manager.
-   *
-   * @var \Drupal\Core\Theme\ThemeManagerInterface
-   */
-  protected $themeManager;
-
-  /**
    * @var \Drupal\Core\Form\FormValidatorInterface
    */
   protected $formValidator;
@@ -124,14 +116,12 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
    *   The request stack.
    * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
    *   The class resolver.
-   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
-   *   The theme manager.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
-   * @param \Drupal\Core\HttpKernel $http_kernel
+   * @param \Symfony\Component\HttpKernel\HttpKernel $http_kernel
    *   The HTTP kernel.
    */
-  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, ModuleHandlerInterface $module_handler, KeyValueExpirableFactoryInterface $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
+  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, ModuleHandlerInterface $module_handler, KeyValueExpirableFactoryInterface $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
     $this->formValidator = $form_validator;
     $this->formSubmitter = $form_submitter;
     $this->moduleHandler = $module_handler;
@@ -141,7 +131,6 @@ public function __construct(FormValidatorInterface $form_validator, FormSubmitte
     $this->classResolver = $class_resolver;
     $this->csrfToken = $csrf_token;
     $this->httpKernel = $http_kernel;
-    $this->themeManager = $theme_manager;
   }
 
   /**
@@ -652,7 +641,20 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
         $form['#theme'][] = $form_state['build_info']['base_form_id'];
       }
     }
+    $this->alterForm($form_id, $form, $form_state);
+  }
 
+  /**
+   * Performs altering of the form.
+   *
+   * @param string $form_id
+   *   A unique string identifying the form.
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  protected function alterForm($form_id, &$form, FormStateInterface &$form_state) {
     // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
     // hook_form_FORM_ID_alter() implementations.
     $hooks = array('form');
@@ -660,8 +662,8 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
       $hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
     }
     $hooks[] = 'form_' . $form_id;
-    $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
-    $this->themeManager->alter($hooks, $form, $form_state, $form_id);
+    $event = new FormAlterEvent($hooks, $form, $form_state, $form_id);
+    $this->eventDispatcher->dispatch(FormEvents::ALTER, $event);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Form/FormEvents.php b/core/lib/Drupal/Core/Form/FormEvents.php
new file mode 100644
index 0000000..4378f8c
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormEvents.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormEvents.
+ */
+
+namespace Drupal\Core\Form;
+
+/**
+ * Defines events for the form system.
+ */
+final class FormEvents {
+
+  /**
+   * Name of event fired when altering a form.
+   *
+   * @see \Drupal\Core\Form\FormBuilder::alterForm()
+   * @see \Drupal\Core\Form\FormAlterEvent
+   */
+  const ALTER = 'form_builder.alter';
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index 108f35a..4950d24 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -139,13 +139,6 @@
    */
   protected $logger;
 
-  /**
-   * The mocked theme manager.
-   *
-   * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $themeManager;
-
   protected function setUp() {
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
 
@@ -170,7 +163,6 @@ protected function setUp() {
       ->disableOriginalConstructor()
       ->getMock();
     $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
     $this->request = new Request();
     $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->requestStack = new RequestStack();
@@ -185,7 +177,7 @@ protected function setUp() {
       ->setMethods(array('batchGet', 'drupalInstallationAttempted'))
       ->getMock();
 
-    $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->themeManager, $this->csrfToken, $this->httpKernel);
+    $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->csrfToken, $this->httpKernel);
     $this->formBuilder->setCurrentUser($this->account);
   }
 
