diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 9f8088a..d553168 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1,11 +1,7 @@
 <?php
 
 use Drupal\Component\Utility\UrlHelper;
-use Drupal\Component\Utility\UserAgent;
-use Drupal\Component\Utility\Crypt;
-
 use Drupal\Component\Utility\Settings;
-use Drupal\Core\Config\FileStorage;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
@@ -674,6 +670,7 @@ function install_tasks($install_state) {
       // we still need to display it if settings.php is invalid in any way,
       // since the form submit handler is where settings.php is rewritten.
       'run' => $install_state['settings_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+      'function' => 'Drupal\Core\Installer\Form\SiteSettingsForm',
     ),
     'install_base_system' => array(
       'run' => $install_state['base_system_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
@@ -695,6 +692,7 @@ function install_tasks($install_state) {
     'install_configure_form' => array(
       'display_name' => t('Configure site'),
       'type' => 'form',
+      'function' => 'Drupal\Core\Installer\Form\SiteConfigureForm',
     ),
   );
 
@@ -809,8 +807,9 @@ function install_get_form($form_id, array &$install_state) {
     ),
     'no_redirect' => TRUE,
   );
+  $form_builder = \Drupal::formBuilder();
   if ($install_state['interactive']) {
-    $form = drupal_build_form($form_id, $form_state);
+    $form = $form_builder->buildForm($form_id, $form_state);
     // If the form submission was not successful, the form needs to be rendered,
     // which means the task is not complete yet.
     if (empty($form_state['executed'])) {
@@ -822,13 +821,14 @@ function install_get_form($form_id, array &$install_state) {
     // For non-interactive installs, submit the form programmatically with the
     // values taken from the installation state.
     $form_state['values'] = array();
-    if (!empty($install_state['forms'][$form_id])) {
-      $form_state['values'] = $install_state['forms'][$form_id];
+    $install_form_id = $form_builder->getFormId($form_id, $form_state);
+    if (!empty($install_state['forms'][$install_form_id])) {
+      $form_state['values'] = $install_state['forms'][$install_form_id];
     }
-    drupal_form_submit($form_id, $form_state);
+    $form_builder->submitForm($form_id, $form_state);
 
     // Throw an exception in case of any form validation error.
-    if ($errors = form_get_errors($form_state)) {
+    if ($errors = $form_builder->getErrors($form_state)) {
       throw new InstallerException(implode("\n", $errors));
     }
   }
@@ -1015,117 +1015,6 @@ function install_verify_database_settings() {
 }
 
 /**
- * Form constructor for a form to configure and rewrite settings.php.
- *
- * @param $install_state
- *   An array of information about the current installation state.
- *
- * @see install_settings_form_validate()
- * @see install_settings_form_submit()
- * @ingroup forms
- */
-function install_settings_form($form, &$form_state, &$install_state) {
-  global $databases;
-
-  $conf_path = './' . conf_path(FALSE);
-  $settings_file = $conf_path . '/settings.php';
-
-  $form['#title'] = t('Database configuration');
-
-  $drivers = drupal_get_database_types();
-  $drivers_keys = array_keys($drivers);
-
-  // If database connection settings have been prepared in settings.php already,
-  // then the existing values need to be taken over.
-  // Note: The installer even executes this form if there is a valid database
-  // connection already, since the submit handler of this form is responsible
-  // for writing all $settings to settings.php (not limited to $databases).
-  if (isset($databases['default']['default'])) {
-    $default_driver = $databases['default']['default']['driver'];
-    $default_options = $databases['default']['default'];
-  }
-  // Otherwise, use the database connection settings from the form input.
-  // For a non-interactive installation, this is derived from the original
-  // $settings array passed into install_drupal().
-  elseif (isset($form_state['input']['driver'])) {
-    $default_driver = $form_state['input']['driver'];
-    $default_options = $form_state['input'][$default_driver];
-  }
-  // If there is no database information at all yet, just suggest the first
-  // available driver as default value, so that its settings form is made
-  // visible via #states when JavaScript is enabled (see below).
-  else {
-    $default_driver = current($drivers_keys);
-    $default_options = array();
-  }
-
-  $form['driver'] = array(
-    '#type' => 'radios',
-    '#title' => t('Database type'),
-    '#required' => TRUE,
-    '#default_value' => $default_driver,
-  );
-  if (count($drivers) == 1) {
-    $form['driver']['#disabled'] = TRUE;
-  }
-
-  // Add driver specific configuration options.
-  foreach ($drivers as $key => $driver) {
-    $form['driver']['#options'][$key] = $driver->name();
-
-    $form['settings'][$key] = $driver->getFormOptions($default_options);
-    $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . t('@driver_name settings', array('@driver_name' => $driver->name())) . '</h2>';
-    $form['settings'][$key]['#type'] = 'container';
-    $form['settings'][$key]['#tree'] = TRUE;
-    $form['settings'][$key]['advanced_options']['#parents'] = array($key);
-    $form['settings'][$key]['#states'] = array(
-      'visible' => array(
-        ':input[name=driver]' => array('value' => $key),
-      )
-    );
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save and continue'),
-    '#button_type' => 'primary',
-    '#limit_validation_errors' => array(
-      array('driver'),
-      array($default_driver),
-    ),
-    '#submit' => array('install_settings_form_submit'),
-  );
-
-  $form['errors'] = array();
-  $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
-
-  return $form;
-}
-
-/**
- * Form validation handler for install_settings_form().
- *
- * @see install_settings_form_submit()
- */
-function install_settings_form_validate($form, &$form_state) {
-  $driver = $form_state['values']['driver'];
-  $database = $form_state['values'][$driver];
-  $drivers = drupal_get_database_types();
-  $reflection = new \ReflectionClass($drivers[$driver]);
-  $install_namespace = $reflection->getNamespaceName();
-  // Cut the trailing \Install from namespace.
-  $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
-  $database['driver'] = $driver;
-
-  $form_state['storage']['database'] = $database;
-  $errors = install_database_errors($database, $form_state['values']['settings_file']);
-  foreach ($errors as $name => $message) {
-    form_set_error($name, $form_state, $message);
-  }
-}
-
-/**
  * Checks a database connection and returns any errors.
  */
 function install_database_errors($database, $settings_file) {
@@ -1167,46 +1056,6 @@ function install_database_errors($database, $settings_file) {
 }
 
 /**
- * Form submission handler for install_settings_form().
- *
- * @see install_settings_form_validate()
- */
-function install_settings_form_submit($form, &$form_state) {
-  global $install_state;
-
-  // Update global settings array and save.
-  $settings = array();
-  $database = $form_state['storage']['database'];
-  $settings['databases']['default']['default'] = (object) array(
-    'value'    => $database,
-    'required' => TRUE,
-  );
-  $settings['settings']['hash_salt'] = (object) array(
-    'value'    => Crypt::randomBytesBase64(55),
-    'required' => TRUE,
-  );
-  // Remember the profile which was used.
-  $settings['settings']['install_profile'] = (object) array(
-    'value' => $install_state['parameters']['profile'],
-    'required' => TRUE,
-  );
-
-  drupal_rewrite_settings($settings);
-
-  // Add the config directories to settings.php.
-  drupal_install_config_directories();
-
-  // Indicate that the settings file has been verified, and check the database
-  // for the last completed task, now that we have a valid connection. This
-  // last step is important since we want to trigger an error if the new
-  // database already has Drupal installed.
-  $install_state['settings_verified'] = TRUE;
-  $install_state['config_verified'] = TRUE;
-  $install_state['database_verified'] = TRUE;
-  $install_state['completed_task'] = install_verify_completed_task();
-}
-
-/**
  * Selects which profile to install.
  *
  * @param $install_state
@@ -1235,7 +1084,7 @@ function install_select_profile(&$install_state) {
         throw new InstallerException(t('Missing profile parameter.'));
       }
       // Otherwise, display a form to select a profile.
-      return install_get_form('install_select_profile_form', $install_state);
+      return install_get_form('Drupal\Core\Installer\Form\SelectProfileForm', $install_state);
     }
   }
 }
@@ -1282,85 +1131,6 @@ function _install_select_profile(&$install_state) {
 }
 
 /**
- * Form constructor for the profile selection form.
- *
- * @param array $install_state
- *   An array of information about the current installation state.
- *
- * @ingroup forms
- */
-function install_select_profile_form($form, &$form_state, $install_state) {
-  $form['#title'] = t('Select an installation profile');
-
-  $profiles = array();
-  $names = array();
-  foreach ($install_state['profiles'] as $profile) {
-    $details = install_profile_info($profile->getName());
-    // Skip this extension if its type is not profile.
-    if (!isset($details['type']) || $details['type'] != 'profile') {
-      continue;
-    }
-    // Don't show hidden profiles. This is used by to hide the testing profile,
-    // which only exists to speed up test runs.
-    if ($details['hidden'] === TRUE) {
-      continue;
-    }
-    $profiles[$profile->getName()] = $details;
-
-    // Determine the name of the profile; default to file name if defined name
-    // is unspecified.
-    $name = isset($details['name']) ? $details['name'] : $profile->getName();
-    $names[$profile->getName()] = $name;
-  }
-
-  // Display radio buttons alphabetically by human-readable name, but always
-  // put the core profiles first (if they are present in the filesystem).
-  natcasesort($names);
-  if (isset($names['minimal'])) {
-    // If the expert ("Minimal") core profile is present, put it in front of
-    // any non-core profiles rather than including it with them alphabetically,
-    // since the other profiles might be intended to group together in a
-    // particular way.
-    $names = array('minimal' => $names['minimal']) + $names;
-  }
-  if (isset($names['standard'])) {
-    // If the default ("Standard") core profile is present, put it at the very
-    // top of the list. This profile will have its radio button pre-selected,
-    // so we want it to always appear at the top.
-    $names = array('standard' => $names['standard']) + $names;
-  }
-
-  // The profile name and description are extracted for translation from the
-  // .info file, so we can use t() on them even though they are dynamic data
-  // at this point.
-  $form['profile'] = array(
-    '#type' => 'radios',
-    '#title' => t('Select an installation profile'),
-    '#title_display' => 'invisible',
-    '#options' => array_map('t', $names),
-    '#default_value' => 'standard',
-  );
-  foreach (array_keys($names) as $profile) {
-    $form['profile'][$profile]['#description'] = isset($profiles[$profile]['description']) ? t($profiles[$profile]['description']) : '';
-  }
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] =  array(
-    '#type' => 'submit',
-    '#value' => t('Save and continue'),
-    '#button_type' => 'primary',
-  );
-  return $form;
-}
-
-/**
- * Form submission handler for install_select_profile_form().
- */
-function install_select_profile_form_submit($form, &$form_state) {
-  global $install_state;
-  $install_state['parameters']['profile'] = $form_state['values']['profile'];
-}
-
-/**
  * Finds all .po files that are useful to the installer.
  *
  * @return
@@ -1427,7 +1197,7 @@ function install_select_language(&$install_state) {
     // translation files were found the form shows a select list of the
     // corresponding languages to choose from.
     if ($install_state['interactive']) {
-      return install_get_form('install_select_language_form', $install_state);
+      return install_get_form('Drupal\Core\Installer\Form\SelectLanguageForm', $install_state);
     }
     // If we are performing a non-interactive installation. If only one language
     // (English) is available, assume the user knows what he is doing. Otherwise
@@ -1445,88 +1215,6 @@ function install_select_language(&$install_state) {
 }
 
 /**
- * Form constructor for the language selection form.
- *
- * @param $install_state
- *   An array of information about the current installation state.
- *
- * @see file_scan_directory()
- * @ingroup forms
- */
-function install_select_language_form($form, &$form_state, &$install_state) {
-  if (count($install_state['translations']) > 1) {
-    $files = $install_state['translations'];
-  }
-  else {
-    $files = array();
-  }
-  $standard_languages = LanguageManager::getStandardLanguageList();
-  $select_options = array();
-  $browser_options = array();
-
-  $form['#title'] = t('Choose language');
-
-  // Build a select list with language names in native language for the user
-  // to choose from. And build a list of available languages for the browser
-  // to select the language default from.
-  if (count($files)) {
-    // Select lists based on available language files.
-    foreach ($files as $langcode => $uri) {
-      $select_options[$langcode] = isset($standard_languages[$langcode]) ? $standard_languages[$langcode][1] : $langcode;
-      $browser_options[] = $langcode;
-    }
-  }
-  else {
-    // Select lists based on all standard languages.
-    foreach ($standard_languages as $langcode => $language_names) {
-      $select_options[$langcode] = $language_names[1];
-      $browser_options[] = $langcode;
-    }
-  }
-
-  $request = Request::createFromGlobals();
-  $browser_langcode = UserAgent::getBestMatchingLangcode($request->server->get('HTTP_ACCEPT_LANGUAGE'), $browser_options);
-  $form['langcode'] = array(
-    '#type' => 'select',
-    '#title' => t('Choose language'),
-    '#title_display' => 'invisible',
-    '#options' => $select_options,
-    // Use the browser detected language as default or English if nothing found.
-    '#default_value' => !empty($browser_langcode) ? $browser_langcode : 'en',
-  );
-
-  if (empty($files)) {
-    $form['help'] = array(
-      '#type' => 'item',
-      '#markup' => \Drupal\Component\Utility\String::format('<p>Translations will be downloaded from the <a href="http://localize.drupal.org">Drupal Translation website</a>.
-        If you do not want this, select <a href="!english">English</a>.</p>', array(
-          '!english' => install_full_redirect_url(array('parameters' => array('langcode' => 'en'))),
-      )),
-      '#states' => array(
-        'invisible' => array(
-          'select[name="langcode"]' => array('value' => 'en'),
-        ),
-      ),
-    );
-  }
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] =  array(
-    '#type' => 'submit',
-    '#value' => t('Save and continue'),
-    '#button_type' => 'primary',
-  );
-  return $form;
-}
-
-/**
- * Form submission handler for the language selection form.
- */
-function install_select_language_form_submit($form, &$form_state) {
-  $install_state = &$form_state['build_info']['args'][0];
-  $install_state['parameters']['langcode'] = $form_state['values']['langcode'];
-}
-
-/**
  * Download a translation file for the selected langaguage.
  *
  * @param array $install_state
@@ -1914,55 +1602,6 @@ function _install_prepare_import($langcode) {
 }
 
 /**
- * Form constructor for a form to configure the new site.
- *
- * @param $install_state
- *   An array of information about the current installation state.
- *
- * @see install_configure_form_validate()
- * @see install_configure_form_submit()
- * @ingroup forms
- */
-function install_configure_form($form, &$form_state, &$install_state) {
-  $form['#title'] = t('Configure site');
-
-  // Warn about settings.php permissions risk
-  $settings_dir = conf_path();
-  $settings_file = $settings_dir . '/settings.php';
-  // Check that $_POST is empty so we only show this message when the form is
-  // first displayed, not on the next page after it is submitted. (We do not
-  // want to repeat it multiple times because it is a general warning that is
-  // not related to the rest of the installation process; it would also be
-  // especially out of place on the last page of the installer, where it would
-  // distract from the message that the Drupal installation has completed
-  // successfully.)
-  $post_params = \Drupal::request()->request->all();
-  if (empty($post_params) && (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) {
-    drupal_set_message(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning');
-  }
-
-  $form['#attached']['library'][] = 'system/drupal.system';
-  // Add JavaScript time zone detection.
-  $form['#attached']['library'][] = 'core/drupal.timezone';
-  // We add these strings as settings because JavaScript translation does not
-  // work during installation.
-  $js = array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail')));
-  $form['#attached']['js'][] = array('data' => $js, 'type' => 'setting');
-
-  // Cache a fully-built schema. This is necessary for any invocation of
-  // index.php because: (1) setting cache table entries requires schema
-  // information, (2) that occurs during bootstrap before any module are
-  // loaded, so (3) if there is no cached schema, drupal_get_schema() will
-  // try to generate one but with no loaded modules will return nothing.
-  //
-  // @todo Move this to the 'install_finished' task?
-  drupal_get_schema(NULL, TRUE);
-
-  // Return the form.
-  return _install_configure_form($form, $form_state, $install_state);
-}
-
-/**
  * Finishes importing files at end of installation.
  *
  * If other projects besides Drupal core have been installed, their translation
@@ -2412,170 +2051,3 @@ function install_display_requirements($install_state, $requirements) {
     }
   }
 }
-
-/**
- * Form constructor for a site configuration form.
- *
- * @param $install_state
- *   An array of information about the current installation state.
- *
- * @see install_configure_form()
- * @see install_configure_form_validate()
- * @see install_configure_form_submit()
- * @ingroup forms
- */
-function _install_configure_form($form, &$form_state, &$install_state) {
-  $form['site_information'] = array(
-    '#type' => 'fieldgroup',
-    '#title' => t('Site information'),
-  );
-  $form['site_information']['site_name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Site name'),
-    '#required' => TRUE,
-    '#weight' => -20,
-  );
-  $form['site_information']['site_mail'] = array(
-    '#type' => 'email',
-    '#title' => t('Site e-mail address'),
-    '#default_value' => ini_get('sendmail_from'),
-    '#description' => t("Automated e-mails, such as registration information, will be sent from this address. Use an address ending in your site's domain to help prevent these e-mails from being flagged as spam."),
-    '#required' => TRUE,
-    '#weight' => -15,
-  );
-
-  $form['admin_account'] = array(
-    '#type' => 'fieldgroup',
-    '#title' => t('Site maintenance account'),
-  );
-  $form['admin_account']['account']['#tree'] = TRUE;
-  $form['admin_account']['account']['name'] = array('#type' => 'textfield',
-    '#title' => t('Username'),
-    '#maxlength' => USERNAME_MAX_LENGTH,
-    '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
-    '#required' => TRUE,
-    '#weight' => -10,
-    '#attributes' => array('class' => array('username')),
-  );
-  $form['admin_account']['account']['mail'] = array(
-    '#type' => 'email',
-    '#title' => t('E-mail address'),
-    '#required' => TRUE,
-    '#weight' => -5,
-  );
-  $form['admin_account']['account']['pass'] = array(
-    '#type' => 'password_confirm',
-    '#required' => TRUE,
-    '#size' => 25,
-    '#weight' => 0,
-  );
-
-  $form['regional_settings'] = array(
-    '#type' => 'fieldgroup',
-    '#title' => t('Regional settings'),
-  );
-  $countries = \Drupal::service('country_manager')->getList();
-  $form['regional_settings']['site_default_country'] = array(
-    '#type' => 'select',
-    '#title' => t('Default country'),
-    '#empty_value' => '',
-    '#default_value' => \Drupal::config('system.date')->get('country.default'),
-    '#options' => $countries,
-    '#description' => t('Select the default country for the site.'),
-    '#weight' => 0,
-  );
-  $form['regional_settings']['date_default_timezone'] = array(
-    '#type' => 'select',
-    '#title' => t('Default time zone'),
-    '#default_value' => date_default_timezone_get(),
-    '#options' => system_time_zones(),
-    '#description' => t('By default, dates in this site will be displayed in the chosen time zone.'),
-    '#weight' => 5,
-    '#attributes' => array('class' => array('timezone-detect')),
-  );
-
-  $form['update_notifications'] = array(
-    '#type' => 'fieldgroup',
-    '#title' => t('Update notifications'),
-  );
-  $form['update_notifications']['update_status_module'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Update notifications'),
-    '#options' => array(
-      1 => t('Check for updates automatically'),
-      2 => t('Receive e-mail notifications'),
-    ),
-    '#default_value' => array(1, 2),
-    '#description' => t('The system will notify you when updates and important security releases are available for installed components. Anonymous information about your site is sent to <a href="@drupal">Drupal.org</a>.', array('@drupal' => 'http://drupal.org')),
-    '#weight' => 15,
-  );
-  $form['update_notifications']['update_status_module'][2] = array(
-    '#states' => array(
-      'visible' => array(
-        'input[name="update_status_module[1]"]' => array('checked' => TRUE),
-      ),
-    ),
-  );
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save and continue'),
-    '#weight' => 15,
-    '#button_type' => 'primary',
-  );
-
-  return $form;
-}
-
-/**
- * Form validation handler for install_configure_form().
- *
- * @see install_configure_form_submit()
- */
-function install_configure_form_validate($form, &$form_state) {
-  if ($error = user_validate_name($form_state['values']['account']['name'])) {
-    form_error($form['admin_account']['account']['name'], $form_state, $error);
-  }
-}
-
-/**
- * Form submission handler for install_configure_form().
- *
- * @see install_configure_form_validate()
- */
-function install_configure_form_submit($form, &$form_state) {
-  \Drupal::config('system.site')
-    ->set('name', $form_state['values']['site_name'])
-    ->set('mail', $form_state['values']['site_mail'])
-    ->save();
-
-  \Drupal::config('system.date')
-    ->set('timezone.default', $form_state['values']['date_default_timezone'])
-    ->set('country.default', $form_state['values']['site_default_country'])
-    ->save();
-
-  // Enable update.module if this option was selected.
-  if ($form_state['values']['update_status_module'][1]) {
-    \Drupal::moduleHandler()->install(array('file', 'update'), FALSE);
-
-    // Add the site maintenance account's email address to the list of
-    // addresses to be notified when updates are available, if selected.
-    if ($form_state['values']['update_status_module'][2]) {
-      \Drupal::config('update.settings')->set('notification.emails', array($form_state['values']['account']['mail']))->save();
-    }
-  }
-
-  // We precreated user 1 with placeholder values. Let's save the real values.
-  $account = user_load(1);
-  $account->init = $account->mail = $form_state['values']['account']['mail'];
-  $account->roles = $account->getRoles();
-  $account->activate();
-  $account->timezone = $form_state['values']['date_default_timezone'];
-  $account->pass = $form_state['values']['account']['pass'];
-  $account->name = $form_state['values']['account']['name'];
-  $account->save();
-
-  // Record when this install ran.
-  \Drupal::state()->set('install_time', $_SERVER['REQUEST_TIME']);
-}
diff --git a/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php b/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php
new file mode 100644
index 0000000..d3667f7
--- /dev/null
+++ b/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Installer\Form\SelectLanguageForm.
+ */
+
+namespace Drupal\Core\Installer\Form;
+
+use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\UserAgent;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Language\LanguageManager;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides the language selection form.
+ */
+class SelectLanguageForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'install_select_language_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state, $install_state = NULL) {
+    if (count($install_state['translations']) > 1) {
+      $files = $install_state['translations'];
+    }
+    else {
+      $files = array();
+    }
+    $standard_languages = LanguageManager::getStandardLanguageList();
+    $select_options = array();
+    $browser_options = array();
+
+    $form['#title'] = $this->t('Choose language');
+
+    // Build a select list with language names in native language for the user
+    // to choose from. And build a list of available languages for the browser
+    // to select the language default from.
+    if (count($files)) {
+      // Select lists based on available language files.
+      foreach ($files as $langcode => $uri) {
+        $select_options[$langcode] = isset($standard_languages[$langcode]) ? $standard_languages[$langcode][1] : $langcode;
+        $browser_options[] = $langcode;
+      }
+    }
+    else {
+      // Select lists based on all standard languages.
+      foreach ($standard_languages as $langcode => $language_names) {
+        $select_options[$langcode] = $language_names[1];
+        $browser_options[] = $langcode;
+      }
+    }
+
+    $request = Request::createFromGlobals();
+    $browser_langcode = UserAgent::getBestMatchingLangcode($request->server->get('HTTP_ACCEPT_LANGUAGE'), $browser_options);
+    $form['langcode'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Choose language'),
+      '#title_display' => 'invisible',
+      '#options' => $select_options,
+      // Use the browser detected language as default or English if nothing found.
+      '#default_value' => !empty($browser_langcode) ? $browser_langcode : 'en',
+    );
+
+    if (empty($files)) {
+      $form['help'] = array(
+        '#type' => 'item',
+        '#markup' => String::format('<p>Translations will be downloaded from the <a href="http://localize.drupal.org">Drupal Translation website</a>.
+        If you do not want this, select <a href="!english">English</a>.</p>', array(
+            '!english' => install_full_redirect_url(array('parameters' => array('langcode' => 'en'))),
+          )),
+        '#states' => array(
+          'invisible' => array(
+            'select[name="langcode"]' => array('value' => 'en'),
+          ),
+        ),
+      );
+    }
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] =  array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save and continue'),
+      '#button_type' => 'primary',
+    );
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $install_state = &$form_state['build_info']['args'][0];
+    $install_state['parameters']['langcode'] = $form_state['values']['langcode'];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Installer/Form/SelectProfileForm.php b/core/lib/Drupal/Core/Installer/Form/SelectProfileForm.php
new file mode 100644
index 0000000..38bb96f
--- /dev/null
+++ b/core/lib/Drupal/Core/Installer/Form/SelectProfileForm.php
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Installer\Form\SelectProfileForm.
+ */
+
+namespace Drupal\Core\Installer\Form;
+
+use Drupal\Core\Form\FormBase;
+
+/**
+ * Provides the profile selection form.
+ */
+class SelectProfileForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'install_select_profile_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state, $install_state = NULL) {
+    $form['#title'] = $this->t('Select an installation profile');
+
+    $profiles = array();
+    $names = array();
+    foreach ($install_state['profiles'] as $profile) {
+      /** @var $profile \Drupal\Core\Extension\Extension */
+      $details = install_profile_info($profile->getName());
+      // Skip this extension if its type is not profile.
+      if (!isset($details['type']) || $details['type'] != 'profile') {
+        continue;
+      }
+      // Don't show hidden profiles. This is used by to hide the testing profile,
+      // which only exists to speed up test runs.
+      if ($details['hidden'] === TRUE) {
+        continue;
+      }
+      $profiles[$profile->getName()] = $details;
+
+      // Determine the name of the profile; default to file name if defined name
+      // is unspecified.
+      $name = isset($details['name']) ? $details['name'] : $profile->getName();
+      $names[$profile->getName()] = $name;
+    }
+
+    // Display radio buttons alphabetically by human-readable name, but always
+    // put the core profiles first (if they are present in the filesystem).
+    natcasesort($names);
+    if (isset($names['minimal'])) {
+      // If the expert ("Minimal") core profile is present, put it in front of
+      // any non-core profiles rather than including it with them alphabetically,
+      // since the other profiles might be intended to group together in a
+      // particular way.
+      $names = array('minimal' => $names['minimal']) + $names;
+    }
+    if (isset($names['standard'])) {
+      // If the default ("Standard") core profile is present, put it at the very
+      // top of the list. This profile will have its radio button pre-selected,
+      // so we want it to always appear at the top.
+      $names = array('standard' => $names['standard']) + $names;
+    }
+
+    // The profile name and description are extracted for translation from the
+    // .info file, so we can use $this->t() on them even though they are dynamic
+    // data at this point.
+    $form['profile'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Select an installation profile'),
+      '#title_display' => 'invisible',
+      '#options' => array_map(array($this, 't'), $names),
+      '#default_value' => 'standard',
+    );
+    foreach (array_keys($names) as $profile_name) {
+      $form['profile'][$profile_name]['#description'] = isset($profiles[$profile_name]['description']) ? $this->t($profiles[$profile_name]['description']) : '';
+    }
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] =  array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save and continue'),
+      '#button_type' => 'primary',
+    );
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    global $install_state;
+    $install_state['parameters']['profile'] = $form_state['values']['profile'];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
new file mode 100644
index 0000000..fdb6783
--- /dev/null
+++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php
@@ -0,0 +1,278 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Installer\Form\SiteConfigureForm.
+ */
+
+namespace Drupal\Core\Installer\Form;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Locale\CountryManagerInterface;
+use Drupal\Core\State\StateInterface;
+use Drupal\user\UserStorageInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides the site configuration form.
+ */
+class SiteConfigureForm extends FormBase {
+
+  /**
+   * The user storage.
+   *
+   * @var \Drupal\user\UserStorageInterface
+   */
+  protected $userStorage;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The country manager.
+   *
+   * @var \Drupal\Core\Locale\CountryManagerInterface
+   */
+  protected $countryManager;
+
+  /**
+   * Constructs a new SiteConfigureForm.
+   *
+   * @param \Drupal\user\UserStorageInterface $user_storage
+   *   The user storage.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Locale\CountryManagerInterface $country_manager
+   *   The country manager.
+   */
+  public function __construct(UserStorageInterface $user_storage, StateInterface $state, ModuleHandlerInterface $module_handler, CountryManagerInterface $country_manager) {
+    $this->userStorage = $user_storage;
+    $this->state = $state;
+    $this->moduleHandler = $module_handler;
+    $this->countryManager = $country_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager')->getStorage('user'),
+      $container->get('state'),
+      $container->get('module_handler'),
+      $container->get('country_manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'install_configure_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $form['#title'] = $this->t('Configure site');
+
+    // Warn about settings.php permissions risk
+    $settings_dir = conf_path();
+    $settings_file = $settings_dir . '/settings.php';
+    // Check that $_POST is empty so we only show this message when the form is
+    // first displayed, not on the next page after it is submitted. (We do not
+    // want to repeat it multiple times because it is a general warning that is
+    // not related to the rest of the installation process; it would also be
+    // especially out of place on the last page of the installer, where it would
+    // distract from the message that the Drupal installation has completed
+    // successfully.)
+    $post_params = $this->getRequest()->request->all();
+    if (empty($post_params) && (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) {
+      drupal_set_message(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning');
+    }
+
+    $form['#attached']['library'][] = 'system/drupal.system';
+    // Add JavaScript time zone detection.
+    $form['#attached']['library'][] = 'core/drupal.timezone';
+    // We add these strings as settings because JavaScript translation does not
+    // work during installation.
+    $js = array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail')));
+    $form['#attached']['js'][] = array('data' => $js, 'type' => 'setting');
+
+    // Cache a fully-built schema. This is necessary for any invocation of
+    // index.php because: (1) setting cache table entries requires schema
+    // information, (2) that occurs during bootstrap before any module are
+    // loaded, so (3) if there is no cached schema, drupal_get_schema() will
+    // try to generate one but with no loaded modules will return nothing.
+    //
+    // @todo Move this to the 'install_finished' task?
+    drupal_get_schema(NULL, TRUE);
+
+    $form['site_information'] = array(
+      '#type' => 'fieldgroup',
+      '#title' => $this->t('Site information'),
+    );
+    $form['site_information']['site_name'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Site name'),
+      '#required' => TRUE,
+      '#weight' => -20,
+    );
+    $form['site_information']['site_mail'] = array(
+      '#type' => 'email',
+      '#title' => $this->t('Site e-mail address'),
+      '#default_value' => ini_get('sendmail_from'),
+      '#description' => $this->t("Automated e-mails, such as registration information, will be sent from this address. Use an address ending in your site's domain to help prevent these e-mails from being flagged as spam."),
+      '#required' => TRUE,
+      '#weight' => -15,
+    );
+
+    $form['admin_account'] = array(
+      '#type' => 'fieldgroup',
+      '#title' => $this->t('Site maintenance account'),
+    );
+    $form['admin_account']['account']['#tree'] = TRUE;
+    $form['admin_account']['account']['name'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Username'),
+      '#maxlength' => USERNAME_MAX_LENGTH,
+      '#description' => $this->t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
+      '#required' => TRUE,
+      '#weight' => -10,
+      '#attributes' => array('class' => array('username')),
+    );
+    $form['admin_account']['account']['mail'] = array(
+      '#type' => 'email',
+      '#title' => $this->t('E-mail address'),
+      '#required' => TRUE,
+      '#weight' => -5,
+    );
+    $form['admin_account']['account']['pass'] = array(
+      '#type' => 'password_confirm',
+      '#required' => TRUE,
+      '#size' => 25,
+      '#weight' => 0,
+    );
+
+    $form['regional_settings'] = array(
+      '#type' => 'fieldgroup',
+      '#title' => $this->t('Regional settings'),
+    );
+    $countries = $this->countryManager->getList();
+    $form['regional_settings']['site_default_country'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Default country'),
+      '#empty_value' => '',
+      '#default_value' => $this->config('system.date')->get('country.default'),
+      '#options' => $countries,
+      '#description' => $this->t('Select the default country for the site.'),
+      '#weight' => 0,
+    );
+    $form['regional_settings']['date_default_timezone'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Default time zone'),
+      '#default_value' => date_default_timezone_get(),
+      '#options' => system_time_zones(),
+      '#description' => $this->t('By default, dates in this site will be displayed in the chosen time zone.'),
+      '#weight' => 5,
+      '#attributes' => array('class' => array('timezone-detect')),
+    );
+
+    $form['update_notifications'] = array(
+      '#type' => 'fieldgroup',
+      '#title' => $this->t('Update notifications'),
+    );
+    $form['update_notifications']['update_status_module'] = array(
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Update notifications'),
+      '#options' => array(
+        1 => $this->t('Check for updates automatically'),
+        2 => $this->t('Receive e-mail notifications'),
+      ),
+      '#default_value' => array(1, 2),
+      '#description' => $this->t('The system will notify you when updates and important security releases are available for installed components. Anonymous information about your site is sent to <a href="@drupal">Drupal.org</a>.', array('@drupal' => 'http://drupal.org')),
+      '#weight' => 15,
+    );
+    $form['update_notifications']['update_status_module'][2] = array(
+      '#states' => array(
+        'visible' => array(
+          'input[name="update_status_module[1]"]' => array('checked' => TRUE),
+        ),
+      ),
+    );
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save and continue'),
+      '#weight' => 15,
+      '#button_type' => 'primary',
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    if ($error = user_validate_name($form_state['values']['account']['name'])) {
+      $this->setFormError('account][name', $form_state, $error);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $this->config('system.site')
+      ->set('name', $form_state['values']['site_name'])
+      ->set('mail', $form_state['values']['site_mail'])
+      ->save();
+
+    $this->config('system.date')
+      ->set('timezone.default', $form_state['values']['date_default_timezone'])
+      ->set('country.default', $form_state['values']['site_default_country'])
+      ->save();
+
+    // Enable update.module if this option was selected.
+    if ($form_state['values']['update_status_module'][1]) {
+      $this->moduleHandler->install(array('file', 'update'), FALSE);
+
+      // Add the site maintenance account's email address to the list of
+      // addresses to be notified when updates are available, if selected.
+      if ($form_state['values']['update_status_module'][2]) {
+        $this->config('update.settings')->set('notification.emails', array($form_state['values']['account']['mail']))->save();
+      }
+    }
+
+    // We precreated user 1 with placeholder values. Let's save the real values.
+    $account = $this->userStorage->load(1);
+    $account->init = $account->mail = $form_state['values']['account']['mail'];
+    $account->roles = $account->getRoles();
+    $account->activate();
+    $account->timezone = $form_state['values']['date_default_timezone'];
+    $account->pass = $form_state['values']['account']['pass'];
+    $account->name = $form_state['values']['account']['name'];
+    $account->save();
+
+    // Record when this install ran.
+    $this->state->set('install_time', $_SERVER['REQUEST_TIME']);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php
new file mode 100644
index 0000000..0d4f599
--- /dev/null
+++ b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Installer\Form\SiteSettingsForm.
+ */
+
+namespace Drupal\Core\Installer\Form;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Form\FormBase;
+
+/**
+ * Provides a form to configure and rewrite settings.php.
+ */
+class SiteSettingsForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'install_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    global $databases;
+
+    $conf_path = './' . conf_path(FALSE);
+    $settings_file = $conf_path . '/settings.php';
+
+    $form['#title'] = $this->t('Database configuration');
+
+    $drivers = drupal_get_database_types();
+    $drivers_keys = array_keys($drivers);
+
+    // If database connection settings have been prepared in settings.php already,
+    // then the existing values need to be taken over.
+    // Note: The installer even executes this form if there is a valid database
+    // connection already, since the submit handler of this form is responsible
+    // for writing all $settings to settings.php (not limited to $databases).
+    if (isset($databases['default']['default'])) {
+      $default_driver = $databases['default']['default']['driver'];
+      $default_options = $databases['default']['default'];
+    }
+    // Otherwise, use the database connection settings from the form input.
+    // For a non-interactive installation, this is derived from the original
+    // $settings array passed into install_drupal().
+    elseif (isset($form_state['input']['driver'])) {
+      $default_driver = $form_state['input']['driver'];
+      $default_options = $form_state['input'][$default_driver];
+    }
+    // If there is no database information at all yet, just suggest the first
+    // available driver as default value, so that its settings form is made
+    // visible via #states when JavaScript is enabled (see below).
+    else {
+      $default_driver = current($drivers_keys);
+      $default_options = array();
+    }
+
+    $form['driver'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Database type'),
+      '#required' => TRUE,
+      '#default_value' => $default_driver,
+    );
+    if (count($drivers) == 1) {
+      $form['driver']['#disabled'] = TRUE;
+    }
+
+    // Add driver specific configuration options.
+    foreach ($drivers as $key => $driver) {
+      $form['driver']['#options'][$key] = $driver->name();
+
+      $form['settings'][$key] = $driver->getFormOptions($default_options);
+      $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . $this->t('@driver_name settings', array('@driver_name' => $driver->name())) . '</h2>';
+      $form['settings'][$key]['#type'] = 'container';
+      $form['settings'][$key]['#tree'] = TRUE;
+      $form['settings'][$key]['advanced_options']['#parents'] = array($key);
+      $form['settings'][$key]['#states'] = array(
+        'visible' => array(
+          ':input[name=driver]' => array('value' => $key),
+        )
+      );
+    }
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['save'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save and continue'),
+      '#button_type' => 'primary',
+      '#limit_validation_errors' => array(
+        array('driver'),
+        array($default_driver),
+      ),
+      '#submit' => array(array($this, 'submitForm')),
+    );
+
+    $form['errors'] = array();
+    $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    $driver = $form_state['values']['driver'];
+    $database = $form_state['values'][$driver];
+    $drivers = drupal_get_database_types();
+    $reflection = new \ReflectionClass($drivers[$driver]);
+    $install_namespace = $reflection->getNamespaceName();
+    // Cut the trailing \Install from namespace.
+    $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
+    $database['driver'] = $driver;
+
+    $form_state['storage']['database'] = $database;
+    $errors = install_database_errors($database, $form_state['values']['settings_file']);
+    foreach ($errors as $name => $message) {
+      $this->setFormError($name, $form_state, $message);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    global $install_state;
+
+    // Update global settings array and save.
+    $settings = array();
+    $database = $form_state['storage']['database'];
+    $settings['databases']['default']['default'] = (object) array(
+      'value'    => $database,
+      'required' => TRUE,
+    );
+    $settings['settings']['hash_salt'] = (object) array(
+      'value'    => Crypt::randomBytesBase64(55),
+      'required' => TRUE,
+    );
+    // Remember the profile which was used.
+    $settings['settings']['install_profile'] = (object) array(
+      'value' => $install_state['parameters']['profile'],
+      'required' => TRUE,
+    );
+
+    drupal_rewrite_settings($settings);
+
+    // Add the config directories to settings.php.
+    drupal_install_config_directories();
+
+    // Indicate that the settings file has been verified, and check the database
+    // for the last completed task, now that we have a valid connection. This
+    // last step is important since we want to trigger an error if the new
+    // database already has Drupal installed.
+    $install_state['settings_verified'] = TRUE;
+    $install_state['config_verified'] = TRUE;
+    $install_state['database_verified'] = TRUE;
+    $install_state['completed_task'] = install_verify_completed_task();
+  }
+
+}
