FormBuilder.php 59.4 KB
Newer Older
1 2 3 4 5
<?php

namespace Drupal\Core\Form;

use Drupal\Component\Utility\Crypt;
6
use Drupal\Component\Utility\Html;
7
use Drupal\Component\Utility\NestedArray;
8
use Drupal\Component\Utility\UrlHelper;
9
use Drupal\Core\Access\AccessResultInterface;
10
use Drupal\Core\Access\CsrfTokenGenerator;
11
use Drupal\Core\DependencyInjection\ClassResolverInterface;
12
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
13
use Drupal\Core\Extension\ModuleHandlerInterface;
14
use Drupal\Core\Form\Exception\BrokenPostRequestException;
15
use Drupal\Core\Render\Element;
16
use Drupal\Core\Render\ElementInfoManagerInterface;
17
use Drupal\Core\Theme\ThemeManagerInterface;
18
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\HttpFoundation\FileBag;
20
use Symfony\Component\HttpFoundation\RequestStack;
21 22 23 24
use Symfony\Component\HttpFoundation\Response;

/**
 * Provides form building and processing.
25 26
 *
 * @ingroup form_api
27
 */
28
class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface {
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
45
   * The request stack.
46
   *
47
   * @var \Symfony\Component\HttpFoundation\RequestStack
48
   */
49
  protected $requestStack;
50

51 52 53 54 55 56 57
  /**
   * The element info manager.
   *
   * @var \Drupal\Core\Render\ElementInfoManagerInterface
   */
  protected $elementInfo;

58 59 60 61 62 63 64
  /**
   * The CSRF token generator to validate the form token.
   *
   * @var \Drupal\Core\Access\CsrfTokenGenerator
   */
  protected $csrfToken;

65 66 67 68 69 70 71
  /**
   * The class resolver.
   *
   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
   */
  protected $classResolver;

72 73 74 75 76 77 78
  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

79 80 81 82 83 84 85
  /**
   * The theme manager.
   *
   * @var \Drupal\Core\Theme\ThemeManagerInterface
   */
  protected $themeManager;

86
  /**
87
   * @var \Drupal\Core\Form\FormValidatorInterface
88
   */
89
  protected $formValidator;
90

91 92 93 94 95
  /**
   * @var \Drupal\Core\Form\FormSubmitterInterface
   */
  protected $formSubmitter;

96 97 98 99 100 101 102
  /**
   * The form cache.
   *
   * @var \Drupal\Core\Form\FormCacheInterface
   */
  protected $formCache;

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
  /**
   * Defines element value callables which are safe to run even when the form
   * state has an invalid CSRF token.
   *
   * Excluded from this list on purpose:
   *  - Drupal\file\Element\ManagedFile::valueCallback
   *  - Drupal\Core\Datetime\Element\Datelist::valueCallback
   *  - Drupal\Core\Datetime\Element\Datetime::valueCallback
   *  - Drupal\Core\Render\Element\ImageButton::valueCallback
   *  - Drupal\file\Plugin\Field\FieldWidget\FileWidget::value
   *  - color_palette_color_value
   *
   * @var array
   */
  protected $safeCoreValueCallables = [
    'Drupal\Core\Render\Element\Checkbox::valueCallback',
    'Drupal\Core\Render\Element\Checkboxes::valueCallback',
    'Drupal\Core\Render\Element\Email::valueCallback',
    'Drupal\Core\Render\Element\FormElement::valueCallback',
    'Drupal\Core\Render\Element\MachineName::valueCallback',
    'Drupal\Core\Render\Element\Number::valueCallback',
    'Drupal\Core\Render\Element\PathElement::valueCallback',
    'Drupal\Core\Render\Element\Password::valueCallback',
    'Drupal\Core\Render\Element\PasswordConfirm::valueCallback',
    'Drupal\Core\Render\Element\Radio::valueCallback',
    'Drupal\Core\Render\Element\Radios::valueCallback',
    'Drupal\Core\Render\Element\Range::valueCallback',
    'Drupal\Core\Render\Element\Search::valueCallback',
    'Drupal\Core\Render\Element\Select::valueCallback',
    'Drupal\Core\Render\Element\Tableselect::valueCallback',
    'Drupal\Core\Render\Element\Table::valueCallback',
    'Drupal\Core\Render\Element\Tel::valueCallback',
    'Drupal\Core\Render\Element\Textarea::valueCallback',
    'Drupal\Core\Render\Element\Textfield::valueCallback',
    'Drupal\Core\Render\Element\Token::valueCallback',
    'Drupal\Core\Render\Element\Url::valueCallback',
    'Drupal\Core\Render\Element\Weight::valueCallback',
  ];

142 143 144
  /**
   * Constructs a new FormBuilder.
   *
145 146
   * @param \Drupal\Core\Form\FormValidatorInterface $form_validator
   *   The form validator.
147 148
   * @param \Drupal\Core\Form\FormSubmitterInterface $form_submitter
   *   The form submission processor.
149
   * @param \Drupal\Core\Form\FormCacheInterface $form_cache
150
   *   The form cache.
151 152 153 154
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
155 156
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
157 158
   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
   *   The class resolver.
159 160
   * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
   *   The element info manager.
161 162
   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
   *   The theme manager.
163 164 165
   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
   *   The CSRF token generator.
   */
166
  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL) {
167
    $this->formValidator = $form_validator;
168
    $this->formSubmitter = $form_submitter;
169
    $this->formCache = $form_cache;
170 171
    $this->moduleHandler = $module_handler;
    $this->eventDispatcher = $event_dispatcher;
172
    $this->requestStack = $request_stack;
173
    $this->classResolver = $class_resolver;
174
    $this->elementInfo = $element_info;
175
    $this->csrfToken = $csrf_token;
176
    $this->themeManager = $theme_manager;
177 178 179 180 181
  }

  /**
   * {@inheritdoc}
   */
182
  public function getFormId($form_arg, FormStateInterface &$form_state) {
183 184
    // If the $form_arg is the name of a class, instantiate it. Don't allow
    // arbitrary strings to be passed to the class resolver.
185
    if (is_string($form_arg) && class_exists($form_arg)) {
186
      $form_arg = $this->classResolver->getInstanceFromDefinition($form_arg);
187
    }
188

189
    if (!is_object($form_arg) || !($form_arg instanceof FormInterface)) {
190
      throw new \InvalidArgumentException("The form argument $form_arg is not a valid form.");
191 192
    }

193
    // Add the $form_arg as the callback object and determine the form ID.
194
    $form_state->setFormObject($form_arg);
195
    if ($form_arg instanceof BaseFormIdInterface) {
196
      $form_state->addBuildInfo('base_form_id', $form_arg->getBaseFormId());
197 198
    }
    return $form_arg->getFormId();
199 200 201 202 203 204
  }

  /**
   * {@inheritdoc}
   */
  public function getForm($form_arg) {
205
    $form_state = new FormState();
206 207 208

    $args = func_get_args();
    // Remove $form_arg from the arguments.
209
    unset($args[0]);
210
    $form_state->addBuildInfo('args', array_values($args));
211

212
    return $this->buildForm($form_arg, $form_state);
213 214 215 216 217
  }

  /**
   * {@inheritdoc}
   */
218
  public function buildForm($form_id, FormStateInterface &$form_state) {
219 220 221
    // Ensure the form ID is prepared.
    $form_id = $this->getFormId($form_id, $form_state);

222 223 224 225 226 227 228 229 230 231 232 233 234 235
    $request = $this->requestStack->getCurrentRequest();

    // Inform $form_state about the request method that's building it, so that
    // it can prevent persisting state changes during HTTP methods for which
    // that is disallowed by HTTP: GET and HEAD.
    $form_state->setRequestMethod($request->getMethod());

    // Initialize the form's user input. The user input should include only the
    // input meant to be treated as part of what is submitted to the form, so
    // we base it on the form's method rather than the request's method. For
    // example, when someone does a GET request for
    // /node/add/article?destination=foo, which is a form that expects its
    // submission method to be POST, the user input during the GET request
    // should be initialized to empty rather than to ['destination' => 'foo'].
236 237
    $input = $form_state->getUserInput();
    if (!isset($input)) {
238
      $input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();
239
      $form_state->setUserInput($input);
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    }

    if (isset($_SESSION['batch_form_state'])) {
      // We've been redirected here after a batch processing. The form has
      // already been processed, but needs to be rebuilt. See _batch_finished().
      $form_state = $_SESSION['batch_form_state'];
      unset($_SESSION['batch_form_state']);
      return $this->rebuildForm($form_id, $form_state);
    }

    // If the incoming input contains a form_build_id, we'll check the cache for
    // a copy of the form in question. If it's there, we don't have to rebuild
    // the form to proceed. In addition, if there is stored form_state data from
    // a previous step, we'll retrieve it so it can be passed on to the form
    // processing code.
255
    $check_cache = isset($input['form_id']) && $input['form_id'] == $form_id && !empty($input['form_build_id']);
256
    if ($check_cache) {
257
      $form = $this->getCache($input['form_build_id'], $form_state);
258 259 260 261 262 263 264 265 266 267
    }

    // If the previous bit of code didn't result in a populated $form object, we
    // are hitting the form for the first time and we need to build it from
    // scratch.
    if (!isset($form)) {
      // If we attempted to serve the form from cache, uncacheable $form_state
      // keys need to be removed after retrieving and preparing the form, except
      // any that were already set prior to retrieving the form.
      if ($check_cache) {
268
        $form_state_before_retrieval = clone $form_state;
269 270 271 272 273
      }

      $form = $this->retrieveForm($form_id, $form_state);
      $this->prepareForm($form_id, $form, $form_state);

274 275
      // self::setCache() removes uncacheable $form_state keys (see properties
      // in \Drupal\Core\Form\FormState) in order for multi-step forms to work
276
      // properly. This means that form processing logic for single-step forms
277
      // using $form_state->isCached() may depend on data stored in those keys
278 279 280
      // during self::retrieveForm()/self::prepareForm(), but form processing
      // should not depend on whether the form is cached or not, so $form_state
      // is adjusted to match what it would be after a
281 282 283 284 285 286 287 288
      // self::setCache()/self::getCache() sequence. These exceptions are
      // allowed to survive here:
      // - always_process: Does not make sense in conjunction with form caching
      //   in the first place, since passing form_build_id as a GET parameter is
      //   not desired.
      // - temporary: Any assigned data is expected to survives within the same
      //   page request.
      if ($check_cache) {
289 290 291
        $cache_form_state = $form_state->getCacheableArray();
        $cache_form_state['always_process'] = $form_state->getAlwaysProcess();
        $cache_form_state['temporary'] = $form_state->getTemporary();
292 293
        $form_state = $form_state_before_retrieval;
        $form_state->setFormState($cache_form_state);
294 295 296
      }
    }

297 298 299 300 301 302
    // If this form is an AJAX request, disable all form redirects.
    $request = $this->requestStack->getCurrentRequest();
    if ($ajax_form_request = $request->query->has(static::AJAX_FORM_REQUEST)) {
      $form_state->disableRedirect();
    }

303 304 305
    // Now that we have a constructed form, process it. This is where:
    // - Element #process functions get called to further refine $form.
    // - User input, if any, gets incorporated in the #value property of the
306
    //   corresponding elements and into $form_state->getValues().
307 308 309 310 311 312 313 314 315
    // - Validation and submission handlers are called.
    // - If this submission is part of a multistep workflow, the form is rebuilt
    //   to contain the information of the next step.
    // - If necessary, the form and form state are cached or re-cached, so that
    //   appropriate information persists to the next page request.
    // All of the handlers in the pipeline receive $form_state by reference and
    // can use it to know or update information about the state of the form.
    $response = $this->processForm($form_id, $form, $form_state);

316 317 318
    // In case the post request exceeds the configured allowed size
    // (post_max_size), the post request is potentially broken. Add some
    // protection against that and at the same time have a nice error message.
319
    if ($ajax_form_request && !$request->request->has('form_id')) {
320 321 322
      throw new BrokenPostRequestException($this->getFileUploadMaxSize());
    }

323 324 325 326 327 328 329
    // After processing the form, if this is an AJAX form request, interrupt
    // form rendering and return by throwing an exception that contains the
    // processed form and form state. This exception will be caught by
    // \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber::onException() and
    // then passed through
    // \Drupal\Core\Form\FormAjaxResponseBuilderInterface::buildResponse() to
    // build a proper AJAX response.
330 331 332
    // Only do this when the form ID matches, since there is no guarantee from
    // $ajax_form_request that it's an AJAX request for this particular form.
    if ($ajax_form_request && $form_state->isProcessingInput() && $request->request->get('form_id') == $form_id) {
333 334 335
      throw new FormAjaxException($form, $form_state);
    }

336 337 338 339 340 341 342 343 344
    // If the form returns a response, skip subsequent page construction by
    // throwing an exception.
    // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
    //
    // @todo Exceptions should not be used for code flow control. However, the
    //   Form API does not integrate with the HTTP Kernel based architecture of
    //   Drupal 8. In order to resolve this issue properly it is necessary to
    //   completely separate form submission from rendering.
    //   @see https://www.drupal.org/node/2367555
345
    if ($response instanceof Response) {
346
      throw new EnforcedResponseException($response);
347 348
    }

349 350
    // If this was a successful submission of a single-step form or the last
    // step of a multi-step form, then self::processForm() issued a redirect to
351 352 353 354 355 356 357 358 359 360 361 362
    // another page, or back to this page, but as a new request. Therefore, if
    // we're here, it means that this is either a form being viewed initially
    // before any user input, or there was a validation error requiring the form
    // to be re-displayed, or we're in a multi-step workflow and need to display
    // the form's next step. In any case, we have what we need in $form, and can
    // return it for rendering.
    return $form;
  }

  /**
   * {@inheritdoc}
   */
363
  public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) {
364
    $form = $this->retrieveForm($form_id, $form_state);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

    // Only GET and POST are valid form methods. If the form receives its input
    // via POST, then $form_state must be persisted when it is rebuilt between
    // submissions. If the form receives its input via GET, then persisting
    // state is forbidden by $form_state->setCached(), and the form must use
    // the URL itself to transfer its state across steps. Although $form_state
    // throws an exception based on the request method rather than the form's
    // method, we base the decision to cache on the form method, because:
    // - It's the form method that defines what the form needs to do to manage
    //   its state.
    // - rebuildForm() should only be called after successful input processing,
    //   which means the request method matches the form method, and if not,
    //   there's some other error, so it's ok if an exception is thrown.
    if ($form_state->isMethodType('POST')) {
      $form_state->setCached();
    }
381 382

    // If only parts of the form will be returned to the browser (e.g., Ajax or
383 384
    // RIA clients), or if the form already had a new build ID regenerated when
    // it was retrieved from the form cache, reuse the existing #build_id.
385 386 387 388
    // Otherwise, a new #build_id is generated, to not clobber the previous
    // build's data in the form cache; also allowing the user to go back to an
    // earlier build, make changes, and re-submit.
    // @see self::prepareForm()
389
    $rebuild_info = $form_state->getRebuildInfo();
390 391 392
    $enforce_old_build_id = isset($old_form['#build_id']) && !empty($rebuild_info['copy']['#build_id']);
    $old_form_is_mutable_copy = isset($old_form['#build_id_old']);
    if ($enforce_old_build_id || $old_form_is_mutable_copy) {
393
      $form['#build_id'] = $old_form['#build_id'];
394 395 396
      if ($old_form_is_mutable_copy) {
        $form['#build_id_old'] = $old_form['#build_id_old'];
      }
397 398
    }
    else {
399 400 401
      if (isset($old_form['#build_id'])) {
        $form['#build_id_old'] = $old_form['#build_id'];
      }
402
      $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
403 404
    }

405 406 407
    // #action defaults to $request->getRequestUri(), but in case of Ajax and
    // other partial rebuilds, the form is submitted to an alternate URL, and
    // the original #action needs to be retained.
408
    if (isset($old_form['#action']) && !empty($rebuild_info['copy']['#action'])) {
409 410 411 412 413 414 415 416 417
      $form['#action'] = $old_form['#action'];
    }

    $this->prepareForm($form_id, $form, $form_state);

    // Caching is normally done in self::processForm(), but what needs to be
    // cached is the $form structure before it passes through
    // self::doBuildForm(), so we need to do it here.
    // @todo For Drupal 8, find a way to avoid this code duplication.
418
    if ($form_state->isCached()) {
419 420 421 422 423
      $this->setCache($form['#build_id'], $form, $form_state);
    }

    // Clear out all group associations as these might be different when
    // re-rendering the form.
424
    $form_state->setGroups([]);
425 426 427 428 429 430 431 432

    // Return a fully built form that is ready for rendering.
    return $this->doBuildForm($form_id, $form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
433 434
  public function getCache($form_build_id, FormStateInterface $form_state) {
    return $this->formCache->getCache($form_build_id, $form_state);
435 436 437 438 439
  }

  /**
   * {@inheritdoc}
   */
440
  public function setCache($form_build_id, $form, FormStateInterface $form_state) {
441
    $this->formCache->setCache($form_build_id, $form, $form_state);
442 443
  }

444 445 446 447 448 449 450
  /**
   * {@inheritdoc}
   */
  public function deleteCache($form_build_id) {
    $this->formCache->deleteCache($form_build_id);
  }

451 452 453
  /**
   * {@inheritdoc}
   */
454
  public function submitForm($form_arg, FormStateInterface &$form_state) {
455 456
    $build_info = $form_state->getBuildInfo();
    if (empty($build_info['args'])) {
457
      $args = func_get_args();
458 459
      // Remove $form and $form_state from the arguments.
      unset($args[0], $args[1]);
460
      $form_state->addBuildInfo('args', array_values($args));
461 462
    }

463
    // Populate FormState::$input with the submitted values before retrieving
464 465 466
    // the form, to be consistent with what self::buildForm() does for
    // non-programmatic submissions (form builder functions may expect it to be
    // there).
467
    $form_state->setUserInput($form_state->getValues());
468

469
    $form_state->setProgrammed();
470 471 472 473

    $form_id = $this->getFormId($form_arg, $form_state);
    $form = $this->retrieveForm($form_id, $form_state);
    // Programmed forms are always submitted.
474
    $form_state->setSubmitted();
475 476

    // Reset form validation.
477
    $form_state->setValidationEnforced();
478
    $form_state->clearErrors();
479 480 481 482 483 484 485 486

    $this->prepareForm($form_id, $form, $form_state);
    $this->processForm($form_id, $form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
487
  public function retrieveForm($form_id, FormStateInterface &$form_state) {
488
    // Record the $form_id.
489
    $form_state->addBuildInfo('form_id', $form_id);
490 491 492 493

    // We save two copies of the incoming arguments: one for modules to use
    // when mapping form ids to constructor functions, and another to pass to
    // the constructor function itself.
494 495
    $build_info = $form_state->getBuildInfo();
    $args = $build_info['args'];
496

497
    $callback = [$form_state->getFormObject(), 'buildForm'];
498

499
    $form = [];
500 501 502
    // Assign a default CSS class name based on $form_id.
    // This happens here and not in self::prepareForm() in order to allow the
    // form constructor function to override or remove the default class.
503
    $form['#attributes']['class'][] = Html::getClass($form_id);
504
    // Same for the base form ID, if any.
505 506
    if (isset($build_info['base_form_id'])) {
      $form['#attributes']['class'][] = Html::getClass($build_info['base_form_id']);
507 508 509 510 511
    }

    // We need to pass $form_state by reference in order for forms to modify it,
    // since call_user_func_array() requires that referenced variables are
    // passed explicitly.
512
    $args = array_merge([$form, &$form_state], $args);
513 514

    $form = call_user_func_array($callback, $args);
515 516 517 518 519 520 521 522
    // If the form returns a response, skip subsequent page construction by
    // throwing an exception.
    // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
    //
    // @todo Exceptions should not be used for code flow control. However, the
    //   Form API currently allows any form builder functions to return a
    //   response.
    //   @see https://www.drupal.org/node/2363189
523
    if ($form instanceof Response) {
524
      throw new EnforcedResponseException($form);
525 526 527 528 529 530 531 532 533
    }
    $form['#form_id'] = $form_id;

    return $form;
  }

  /**
   * {@inheritdoc}
   */
534
  public function processForm($form_id, &$form, FormStateInterface &$form_state) {
535
    $form_state->setValues([]);
536

537
    // With GET, these forms are always submitted if requested.
538
    if ($form_state->isMethodType('get') && $form_state->getAlwaysProcess()) {
539
      $input = $form_state->getUserInput();
540 541
      if (!isset($input['form_build_id'])) {
        $input['form_build_id'] = $form['#build_id'];
542
      }
543 544
      if (!isset($input['form_id'])) {
        $input['form_id'] = $form_id;
545
      }
546 547
      if (!isset($input['form_token']) && isset($form['#token'])) {
        $input['form_token'] = $this->csrfToken->get($form['#token']);
548
      }
549
      $form_state->setUserInput($input);
550 551 552 553
    }

    // self::doBuildForm() finishes building the form by calling element
    // #process functions and mapping user input, if any, to #value properties,
554 555
    // and also storing the values in $form_state->getValues(). We need to
    // retain the unprocessed $form in case it needs to be cached.
556 557 558 559
    $unprocessed_form = $form;
    $form = $this->doBuildForm($form_id, $form, $form_state);

    // Only process the input if we have a correct form submission.
560
    if ($form_state->isProcessingInput()) {
561 562 563 564 565 566
      // Form values for programmed form submissions typically do not include a
      // value for the submit button. But without a triggering element, a
      // potentially existing #limit_validation_errors property on the primary
      // submit button is not taken account. Therefore, check whether there is
      // exactly one submit button in the form, and if so, automatically use it
      // as triggering_element.
567 568 569
      $buttons = $form_state->getButtons();
      if ($form_state->isProgrammed() && !$form_state->getTriggeringElement() && count($buttons) == 1) {
        $form_state->setTriggeringElement(reset($buttons));
570
      }
571
      $this->formValidator->validateForm($form_id, $form, $form_state);
572

573 574 575 576 577
      // \Drupal\Component\Utility\Html::getUniqueId() maintains a cache of
      // element IDs it has seen, so it can prevent duplicates. We want to be
      // sure we reset that cache when a form is processed, so scenarios that
      // result in the form being built behind the scenes and again for the
      // browser don't increment all the element IDs needlessly.
578
      if (!FormState::hasAnyErrors()) {
579
        // In case of errors, do not break HTML IDs of other forms.
580
        Html::resetSeenIds();
581 582
      }

583
      // If there are no errors and the form is not rebuilding, submit the form.
584
      if (!$form_state->isRebuilding() && !FormState::hasAnyErrors()) {
585 586 587 588 589 590 591
        $submit_response = $this->formSubmitter->doSubmitForm($form, $form_state);
        // If this form was cached, delete it from the cache after submission.
        if ($form_state->isCached()) {
          $this->deleteCache($form['#build_id']);
        }
        // If the form submission directly returned a response, return it now.
        if ($submit_response) {
592
          return $submit_response;
593 594 595 596
        }
      }

      // Don't rebuild or cache form submissions invoked via self::submitForm().
597
      if ($form_state->isProgrammed()) {
598 599 600
        return;
      }

601 602 603 604 605 606 607 608 609 610 611
      // If $form_state->isRebuilding() has been set and input has been
      // processed without validation errors, we are in a multi-step workflow
      // that is not yet complete. A new $form needs to be constructed based on
      // the changes made to $form_state during this request. Normally, a submit
      // handler sets $form_state->isRebuilding() if a fully executed form
      // requires another step. However, for forms that have not been fully
      // executed (e.g., Ajax submissions triggered by non-buttons), there is no
      // submit handler to set $form_state->isRebuilding(). It would not make
      // sense to redisplay the identical form without an error for the user to
      // correct, so we also rebuild error-free non-executed forms, regardless
      // of $form_state->isRebuilding().
612 613 614 615
      // @todo Simplify this logic; considering Ajax and non-HTML front-ends,
      //   along with element-level #submit properties, it makes no sense to
      //   have divergent form execution based on whether the triggering element
      //   has #executes_submit_callback set to TRUE.
616
      if (($form_state->isRebuilding() || !$form_state->isExecuted()) && !FormState::hasAnyErrors()) {
617
        // Form building functions (e.g., self::handleInputElement()) may use
618
        // $form_state->isRebuilding() to determine if they are running in the
619
        // context of a rebuild, so ensure it is set.
620
        $form_state->setRebuild();
621 622 623 624 625
        $form = $this->rebuildForm($form_id, $form_state, $form);
      }
    }

    // After processing the form, the form builder or a #process callback may
626 627 628 629 630 631 632
    // have called $form_state->setCached() to indicate that the form and form
    // state shall be cached. But the form may only be cached if
    // $form_state->disableCache() is not called. Only cache $form as it was
    // prior to self::doBuildForm(), because self::doBuildForm() must run for
    // each request to accommodate new user input. Rebuilt forms are not cached
    // here, because self::rebuildForm() already takes care of that.
    if (!$form_state->isRebuilding() && $form_state->isCached()) {
633 634 635 636
      $this->setCache($form['#build_id'], $unprocessed_form, $form_state);
    }
  }

637 638 639 640 641 642 643 644 645 646 647 648 649 650
  /**
   * #lazy_builder callback; renders a form action URL.
   *
   * @return array
   *   A renderable array representing the form action.
   */
  public function renderPlaceholderFormAction() {
    return [
      '#type' => 'markup',
      '#markup' => $this->buildFormAction(),
      '#cache' => ['contexts' => ['url.path', 'url.query_args']],
    ];
  }

651 652 653 654
  /**
   * #lazy_builder callback; renders form CSRF token.
   *
   * @param string $placeholder
655 656
   *   A string containing a placeholder, matching the value of the form's
   *   #token.
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
   *
   * @return array
   *   A renderable array containing the CSRF token.
   */
  public function renderFormTokenPlaceholder($placeholder) {
    return [
      '#markup' => $this->csrfToken->get($placeholder),
      '#cache' => [
        'contexts' => [
          'session',
        ],
      ],
    ];
  }

672 673 674
  /**
   * {@inheritdoc}
   */
675
  public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
676 677 678 679
    $user = $this->currentUser();

    $form['#type'] = 'form';

680 681
    // Only update the action if it is not already set.
    if (!isset($form['#action'])) {
682 683 684 685
      // Instead of setting an actual action URL, we set the placeholder, which
      // will be replaced at the very last moment. This ensures forms with
      // dynamically generated action URLs don't have poor cacheability.
      // Use the proper API to generate the placeholder, when we have one. See
686 687 688
      // https://www.drupal.org/node/2562341. The placholder uses a fixed string
      // that is Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm');
      $placeholder = 'form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM';
689 690 691 692 693

      $form['#attached']['placeholders'][$placeholder] = [
        '#lazy_builder' => ['form_builder:renderPlaceholderFormAction', []],
      ];
      $form['#action'] = $placeholder;
694 695
    }

696
    // Fix the form method, if it is 'get' in $form_state, but not in $form.
697
    if ($form_state->isMethodType('get') && !isset($form['#method'])) {
698 699 700
      $form['#method'] = 'get';
    }

701 702 703 704 705 706 707 708 709
    // GET forms should not use a CSRF token.
    if (isset($form['#method']) && $form['#method'] === 'get') {
      // Merges in a default, this means if you've explicitly set #token to the
      // the $form_id on a GET form, which we don't recommend, it will work.
      $form += [
        '#token' => FALSE,
      ];
    }

710 711 712 713 714 715 716
    // Generate a new #build_id for this form, if none has been set already.
    // The form_build_id is used as key to cache a particular build of the form.
    // For multi-step forms, this allows the user to go back to an earlier
    // build, make changes, and re-submit.
    // @see self::buildForm()
    // @see self::rebuildForm()
    if (!isset($form['#build_id'])) {
717
      $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
718
    }
719
    $form['form_build_id'] = [
720 721 722 723 724 725 726
      '#type' => 'hidden',
      '#value' => $form['#build_id'],
      '#id' => $form['#build_id'],
      '#name' => 'form_build_id',
      // Form processing and validation requires this value, so ensure the
      // submitted form value appears literally, regardless of custom #tree
      // and #parents being set elsewhere.
727
      '#parents' => ['form_build_id'],
728 729 730 731 732 733 734 735 736 737 738
      // Prevent user agents from prefilling the build id with earlier values.
      // When the ajax command "update_build_id" is executed, the user agent
      // will assume that a user interaction changed the field. Upon a soft
      // reload of the page, the previous build id will be restored in the
      // input, causing subsequent ajax callbacks to access the wrong cached
      // form build. Setting the autocomplete attribute to "off" will tell the
      // user agent to never reuse the value.
      // @see https://www.w3.org/TR/2011/WD-html5-20110525/common-input-element-attributes.html#the-autocomplete-attribute
      '#attributes' => [
        'autocomplete' => 'off',
      ],
739
    ];
740 741 742 743 744 745 746 747 748

    // Add a token, based on either #token or form_id, to any form displayed to
    // authenticated users. This ensures that any submitted form was actually
    // requested previously by the user and protects against cross site request
    // forgeries.
    // This does not apply to programmatically submitted forms. Furthermore,
    // since tokens are session-bound and forms displayed to anonymous users are
    // very likely cached, we cannot assign a token for them.
    // During installation, there is no $user yet.
749 750 751 752 753
    // Form constructors may explicitly set #token to FALSE when cross site
    // request forgery is irrelevant to the form, such as search forms.
    if ($form_state->isProgrammed() || (isset($form['#token']) && $form['#token'] === FALSE)) {
      unset($form['#token']);
    }
754 755 756 757
    else {
      $form['#cache']['contexts'][] = 'user.roles:authenticated';
      if ($user && $user->isAuthenticated()) {
        // Generate a public token based on the form id.
758
        // Generates a placeholder based on the form ID.
759
        $placeholder = 'form_token_placeholder_' . Crypt::hashBase64($form_id);
760 761
        $form['#token'] = $placeholder;

762
        $form['form_token'] = [
763 764
          '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
          '#type' => 'token',
765
          '#default_value' => $placeholder,
766 767 768
          // Form processing and validation requires this value, so ensure the
          // submitted form value appears literally, regardless of custom #tree
          // and #parents being set elsewhere.
769
          '#parents' => ['form_token'],
770 771 772 773 774 775 776 777 778 779 780
          // Instead of setting an actual CSRF token, we've set the placeholder
          // in form_token's #default_value and #placeholder. These will be
          // replaced at the very last moment. This ensures forms with a CSRF
          // token don't have poor cacheability.
          '#attached' => [
            'placeholders' => [
              $placeholder => [
                '#lazy_builder' => ['form_builder:renderFormTokenPlaceholder', [$placeholder]]
              ]
            ]
          ],
781 782 783
          '#cache' => [
            'max-age' => 0,
          ],
784
        ];
785
      }
786 787 788
    }

    if (isset($form_id)) {
789
      $form['form_id'] = [
790 791
        '#type' => 'hidden',
        '#value' => $form_id,
792
        '#id' => Html::getUniqueId("edit-$form_id"),
793 794 795
        // Form processing and validation requires this value, so ensure the
        // submitted form value appears literally, regardless of custom #tree
        // and #parents being set elsewhere.
796 797
        '#parents' => ['form_id'],
      ];
798 799
    }
    if (!isset($form['#id'])) {
800
      $form['#id'] = Html::getUniqueId($form_id);
801 802 803
      // Provide a selector usable by JavaScript. As the ID is unique, its not
      // possible to rely on it in JavaScript.
      $form['#attributes']['data-drupal-selector'] = Html::getId($form_id);
804 805
    }

806
    $form += $this->elementInfo->getInfo('form');
807
    $form += ['#tree' => FALSE, '#parents' => []];
808 809
    $form['#validate'][] = '::validateForm';
    $form['#submit'][] = '::submitForm';
810

811
    $build_info = $form_state->getBuildInfo();
812
    // If no #theme has been set, automatically apply theme suggestions.
813 814 815
    // The form theme hook itself, which is rendered by form.html.twig,
    // is in #theme_wrappers. Therefore, the #theme function only has to care
    // for rendering the inner form elements, not the form itself.
816
    if (!isset($form['#theme'])) {
817
      $form['#theme'] = [$form_id];
818 819
      if (isset($build_info['base_form_id'])) {
        $form['#theme'][] = $build_info['base_form_id'];
820 821 822 823 824
      }
    }

    // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
    // hook_form_FORM_ID_alter() implementations.
825
    $hooks = ['form'];
826 827
    if (isset($build_info['base_form_id'])) {
      $hooks[] = 'form_' . $build_info['base_form_id'];
828 829 830
    }
    $hooks[] = 'form_' . $form_id;
    $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
831
    $this->themeManager->alter($hooks, $form, $form_state, $form_id);
832 833
  }

834 835 836 837 838 839 840 841 842
  /**
   * Builds the $form['#action'].
   *
   * @return string
   *   The URL to be used as the $form['#action'].
   */
  protected function buildFormAction() {
    // @todo Use <current> instead of the master request in
    //   https://www.drupal.org/node/2505339.
843 844 845 846 847 848 849 850
    $request = $this->requestStack->getMasterRequest();
    $request_uri = $request->getRequestUri();

    // Prevent cross site requests via the Form API by using an absolute URL
    // when the request uri starts with multiple slashes..
    if (strpos($request_uri, '//') === 0) {
      $request_uri = $request->getUri();
    }
851 852 853 854 855 856 857 858

    // @todo Remove this parsing once these are removed from the request in
    //   https://www.drupal.org/node/2504709.
    $parsed = UrlHelper::parse($request_uri);
    unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]);
    return $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : '');
  }

859 860 861 862 863 864 865
  /**
   * {@inheritdoc}
   */
  public function setInvalidTokenError(FormStateInterface $form_state) {
    $this->formValidator->setInvalidTokenError($form_state);
  }

866 867 868
  /**
   * {@inheritdoc}
   */
869
  public function validateForm($form_id, &$form, FormStateInterface &$form_state) {
870
    $this->formValidator->validateForm($form_id, $form, $form_state);
871 872
  }

873 874 875
  /**
   * {@inheritdoc}
   */
876
  public function redirectForm(FormStateInterface $form_state) {
877
    return $this->formSubmitter->redirectForm($form_state);
878 879 880
  }

  /**
881
   * {@inheritdoc}
882
   */
883
  public function executeValidateHandlers(&$form, FormStateInterface &$form_state) {
884
    $this->formValidator->executeValidateHandlers($form, $form_state);
885 886 887 888 889
  }

  /**
   * {@inheritdoc}
   */
890
  public function executeSubmitHandlers(&$form, FormStateInterface &$form_state) {
891 892
    $this->formSubmitter->executeSubmitHandlers($form, $form_state);
  }
893

894 895 896
  /**
   * {@inheritdoc}
   */
897
  public function doSubmitForm(&$form, FormStateInterface &$form_state) {
898
    throw new \LogicException('Use FormBuilderInterface::processForm() instead.');
899 900 901 902 903
  }

  /**
   * {@inheritdoc}
   */
904
  public function doBuildForm($form_id, &$element, FormStateInterface &$form_state) {
905 906 907 908
    // Initialize as unprocessed.
    $element['#processed'] = FALSE;

    // Use element defaults.
909
    if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element['#type']))) {
910 911 912 913 914
      // Overlay $info onto $element, retaining preexisting keys in $element.
      $element += $info;
      $element['#defaults_loaded'] = TRUE;
    }
    // Assign basic defaults common for all form elements.
915
    $element += [
916
      '#required' => FALSE,
917
      '#attributes' => [],
918
      '#title_display' => 'before',
919
      '#description_display' => 'after',
920
      '#errors' => NULL,
921
    ];
922 923 924

    // Special handling if we're on the top level form element.
    if (isset($element['#type']) && $element['#type'] == 'form') {
925
      if (!empty($element['#https']) && !UrlHelper::isExternal($element['#action'])) {
926 927 928 929 930 931 932 933 934
        global $base_root;

        // Not an external URL so ensure that it is secure.
        $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
      }

      // Store a reference to the complete form in $form_state prior to building
      // the form. This allows advanced #process and #after_build callbacks to
      // perform changes elsewhere in the form.
935
      $form_state->setCompleteForm($element);
936 937 938 939

      // Set a flag if we have a correct form submission. This is always TRUE
      // for programmed forms coming from self::submitForm(), or if the form_id
      // coming from the POST data is set and matches the current form_id.
940
      $input = $form_state->getUserInput();
941 942
      if ($form_state->isProgrammed() || (!empty($input) && (isset($input['form_id']) && ($input['form_id'] == $form_id)))) {
        $form_state->setProcessInput();
943 944 945 946 947 948 949 950 951 952 953 954 955 956
        if (isset($element['#token'])) {
          $input = $form_state->getUserInput();
          if (empty($input['form_token']) || !$this->csrfToken->validate($input['form_token'], $element['#token'])) {
            // Set an early form error to block certain input processing since
            // that opens the door for CSRF vulnerabilities.
            $this->setInvalidTokenError($form_state);

            // This value is checked in self::handleInputElement().
            $form_state->setInvalidToken(TRUE);

            // Make sure file uploads do not get processed.
            $this->requestStack->getCurrentRequest()->files = new FileBag();
          }
        }
957 958
      }
      else {
959
        $form_state->setProcessInput(FALSE);
960 961 962
      }

      // All form elements should have an #array_parents property.
963
      $element['#array_parents'] = [];
964 965 966
    }

    if (!isset($element['#id'])) {
967 968 969 970 971 972 973 974 975 976
      $unprocessed_id = 'edit-' . implode('-', $element['#parents']);
      $element['#id'] = Html::getUniqueId($unprocessed_id);
      // Provide a selector usable by JavaScript. As the ID is unique, its not
      // possible to rely on it in JavaScript.
      $element['#attributes']['data-drupal-selector'] = Html::getId($unprocessed_id);
    }
    else {
      // Provide a selector usable by JavaScript. As the ID is unique, its not
      // possible to rely on it in JavaScript.
      $element['#attributes']['data-drupal-selector'] = Html::getId($element['#id']);
977 978 979 980 981 982 983 984 985 986 987 988 989 990
    }

    // Add the aria-describedby attribute to associate the form control with its
    // description.
    if (!empty($element['#description'])) {
      $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
    }
    // Handle input elements.
    if (!empty($element['#input'])) {
      $this->handleInputElement($form_id, $element, $form_state);
    }
    // Allow for elements to expand to multiple elements, e.g., radios,
    // checkboxes and files.
    if (isset($element['#process']) && !$element['#processed']) {
991
      foreach ($element['#process'] as $callback) {
992
        $complete_form = &$form_state->getCompleteForm();
993
        $element = call_user_func_array($form_state->prepareCallback($callback), [&$element, &$form_state, &$complete_form]);
994 995 996 997 998 999 1000 1001 1002
      }
      $element['#processed'] = TRUE;
    }

    // We start off assuming all form elements are in the correct order.
    $element['#sorted'] = TRUE;

    // Recurse through all child elements.
    $count = 0;
1003 1004 1005 1006 1007 1008 1009
    if (isset($element['#access'])) {
      $access = $element['#access'];
      $inherited_access = NULL;
      if (($access instanceof AccessResultInterface && !$access->isAllowed()) || $access === FALSE) {
        $inherited_access = $access;
      }
    }
1010
    foreach (Element::children($element) as $key) {
1011 1012
      // Prior to checking properties of child elements, their default
      // properties need to be loaded.
1013
      if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element[$key]['#type']))) {
1014 1015 1016 1017 1018 1019 1020 1021 1022
        $element[$key] += $info;
        $element[$key]['#defaults_loaded'] = TRUE;
      }

      // Don't squash an existing tree value.
      if (!isset($element[$key]['#tree'])) {
        $element[$key]['#tree'] = $element['#tree'];
      }

1023 1024 1025
      // Children inherit #access from parent.
      if (isset($inherited_access)) {
        $element[$key]['#access'] = $inherited_access;
1026 1027 1028 1029
      }

      // Make child elements inherit their parent's #disabled and #allow_focus
      // values unless they specify their own.
1030
      foreach (['#disabled', '#allow_focus'] as $property) {
1031 1032 1033 1034 1035 1036 1037 1038 1039
        if (isset($element[$property]) && !isset($element[$key][$property])) {
          $element[$key][$property] = $element[$property];
        }
      }

      // Don't squash existing parents value.
      if (!isset($element[$key]['#parents'])) {
        // Check to see if a tree of child elements is present. If so,
        // continue down the tree if required.
1040
        $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], [$key]) : [$key];
1041 1042 1043 1044 1045 1046 1047 1048
      }
      // Ensure #array_parents follows the actual form structure.
      $array_parents = $element['#array_parents'];
      $array_parents[] = $key;
      $element[$key]['#array_parents'] = $array_parents;

      // Assign a decimal placeholder weight to preserve original array order.
      if (!isset($element[$key]['#weight'])) {
1049
        $element[$key]['#weight'] = $count / 1000;
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
      }
      else {
        // If one of the child elements has a weight then we will need to sort
        // later.
        unset($element['#sorted']);
      }
      $element[$key] = $this->doBuildForm($form_id, $element[$key], $form_state);
      $count++;
    }

    // The #after_build flag allows any piece of a form to be altered
    // after normal input parsing has been completed.
    if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
1063
      foreach ($element['#after_build'] as $callback) {
1064
        $element = call_user_func_array($form_state->prepareCallback($callback), [$element, &$form_state]);
1065 1066 1067 1068 1069 1070 1071
      }
      $element['#after_build_done'] = TRUE;
    }

    // If there is a file element, we need to flip a flag so later the
    // form encoding can be set.
    if (isset($element['#type']) && $element['#type'] == 'file') {
1072
      $form_state->setHasFileElement();
1073 1074 1075 1076 1077 1078
    }

    // Final tasks for the form element after self::doBuildForm() has run for
    // all other elements.
    if (isset($element['#type']) && $element['#type'] == 'form') {
      // If there is a file element, we set the form encoding.
1079
      if ($form_state->hasFileElement()) {
1080 1081 1082
        $element['#attributes']['enctype'] = 'multipart/form-data';
      }

1083 1084 1085 1086 1087
      // Allow Ajax submissions to the form action to bypass verification. This
      // is especially useful for multipart forms, which cannot be verified via
      // a response header.
      $element['#attached']['drupalSettings']['ajaxTrustedUrl'][$element['#action']] = TRUE;

1088 1089 1090 1091 1092 1093
      // If a form contains a single textfield, and the ENTER key is pressed
      // within it, Internet Explorer submits the form with no POST data
      // identifying any submit button. Other browsers submit POST data as
      // though the user clicked the first button. Therefore, to be as
      // consistent as we can be across browsers, if no 'triggering_element' has
      // been identified yet, default it to the first button.
1094 1095 1096
      $buttons = $form_state->getButtons();
      if (!$form_state->isProgrammed() && !$form_state->getTriggeringElement() && !empty($buttons)) {
        $form_state->setTriggeringElement($buttons[0]);
1097 1098
      }

1099
      $triggering_element = $form_state->getTriggeringElement();
1100 1101 1102
      // If the triggering element specifies "button-level" validation and
      // submit handlers to run instead of the default form-level ones, then add
      // those to the form state.
1103 1104 1105 1106 1107
      if (isset($triggering_element['#validate'])) {
        $form_state->setValidateHandlers($triggering_element['#validate']);
      }
      if (isset($triggering_element['#submit'])) {
        $form_state->setSubmitHandlers($triggering_element['#submit']);
1108 1109 1110 1111
      }

      // If the triggering element executes submit handlers, then set the form
      // state key that's needed for those handlers to run.
1112
      if (!empty($triggering_element['#executes_submit_callback'])) {
1113
        $form_state->setSubmitted();
1114 1115 1116
      }

      // Special processing if the triggering element is a button.
1117
      if (!empty($triggering_element['#is_button'])) {
1118 1119 1120 1121 1122 1123
        // Because there are several ways in which the triggering element could
        // have been determined (including from input variables set by
        // JavaScript or fallback behavior implemented for IE), and because
        // buttons often have their #name property not derived from their
        // #parents property, we can't assume that input processing that's
        // happened up until here has resulted in
1124
        // $form_state->getValue(BUTTON_NAME) being set. But it's common for
1125
        // forms to have several buttons named 'op' and switch on
1126 1127
        // $form_state->getValue('op') during submit handler execution.
        $form_state->setValue($triggering_element['#name'], $triggering_element['#value']);
1128 1129 1130 1131 1132
      }
    }
    return $element;
  }

1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
  /**
   * Helper function to normalize the different callable formats.
   *
   * @param callable $value_callable
   *   The callable to be checked.
   *
   * @return bool
   *   TRUE if the callable is safe even if the CSRF token is invalid, FALSE
   *   otherwise.
   */
  protected function valueCallableIsSafe(callable $value_callable) {
    // The same static class method callable may be formatted in two array and
    // two string forms:
    // ['\Classname', 'methodname']
    // ['Classname', 'methodname']
    // '\Classname::methodname'
    // 'Classname::methodname'
    if (is_callable($value_callable, FALSE, $callable_name)) {
      // The third parameter of is_callable() is set to a string form, but we
      // still have to normalize further by stripping a leading '\'.
      return in_array(ltrim($callable_name, '\\'), $this->safeCoreValueCallables);
    }
    return FALSE;
  }

1158 1159 1160
  /**
   * Adds the #name and #value properties of an input element before rendering.
   */
1161
  protected function handleInputElement($form_id, &$element, FormStateInterface &$form_state) {
1162 1163 1164 1165
    if (!isset($element['#name'])) {
      $name = array_shift($element['#parents']);
      $element['#name'] = $name;
      if ($element['#type'] == 'file') {
1166
        // To make it easier to handle files in file.inc, we place all
1167 1168
        // file fields in the 'files' array. Also, we do not support
        // nested file names.
1169
        // @todo Remove this files prefix now?
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
        $element['#name'] = 'files[' . $element['#name'] . ']';
      }
      elseif (count($element['#parents'])) {
        $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
      }
      array_unshift($element['#parents'], $name);
    }

    // Setting #disabled to TRUE results in user input being ignored regardless
    // of how the element is themed or whether JavaScript is used to change the
    // control's attributes. However, it's good UI to let the user know that
    // input is not wanted for the control. HTML supports two attributes for:
    // this: http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form
    // wants to start a control off with one of these attributes for UI
    // purposes, only, but still allow input to be processed if it's submitted,
    // it can set the desired attribute in #attributes directly rather than
    // using #disabled. However, developers should think carefully about the
    // accessibility implications of doing so: if the form expects input to be
    // enterable under some condition triggered by JavaScript, how would someone
    // who has JavaScript disabled trigger that condition? Instead, developers
    // should consider whether a multi-step form would be more appropriate
    // (#disabled can be changed from step to step). If one still decides to use
    // JavaScript to affect when a control is enabled, then it is best for
    // accessibility for the control to be enabled in the HTML, and disabled by
    // JavaScript on document ready.
    if (!empty($element['#disabled'])) {
      if (!empty($element['#allow_focus'])) {
        $element['#attributes']['readonly'] = 'readonly';
      }
      else {
        $element['#attributes']['disabled'] = 'disabled';
      }
    }

    // With JavaScript or other easy hacking, input can be submitted even for
    // elements with #access=FALSE or #disabled=TRUE. For security, these must
    // not be processed. Forms that set #disabled=TRUE on an element do not
    // expect input for the element, and even forms submitted with
    // self::submitForm() must not be able to get around this. Forms that set
    // #access=FALSE on an element usually allow access for some users, so forms
    // submitted with self::submitForm() may bypass access restriction and be
    // treated as high-privilege users instead.
1212
    $process_input = empty($element['#disabled']) && (($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks()) || ($form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access'])));
1213 1214 1215

    // Set the element's #value property.
    if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
1216 1217 1218
      // @todo Once all elements are converted to plugins in
      //   https://www.drupal.org/node/2311393, rely on
      //   $element['#value_callback'] directly.
1219
      $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
1220 1221 1222 1223
      if (!is_callable($value_callable)) {
        $value_callable = '\Drupal\Core\Render\Element\FormElement::valueCallback';
      }

1224 1225 1226 1227
      if ($process_input) {
        // Get the input for the current element. NULL values in the input need
        // to be explicitly distinguished from missing input. (see below)
        $input_exists = NULL;
1228
        $input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'], $input_exists);
1229 1230 1231
        // For browser-submitted forms, the submitted values do not contain
        // values for certain elements (empty multiple select, unchecked
        // checkbox). During initial form processing, we add explicit NULL
1232
        // values for such elements in FormState::$input. When rebuilding the
1233 1234 1235 1236
        // form, we can distinguish elements having NULL input from elements
        // that were not part of the initially submitted form and can therefore
        // use default values for the latter, if required. Programmatically
        // submitted forms can submit explicit NULL values when calling
1237
        // self::submitForm() so we do not modify FormState::$input for them.
1238
        if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) {
1239
          // Add the necessary parent keys to FormState::$input and sets the
1240
          // element's input value to NULL.
1241
          NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
1242 1243 1244 1245 1246
          $input_exists = TRUE;
        }
        // If we have input for the current element, assign it to the #value
        // property, optionally filtered through $value_callback.
        if ($input_exists) {
1247 1248 1249
          // Skip all value callbacks except safe ones like text if the CSRF
          // token was invalid.
          if (!$form_state->hasInvalidToken() || $this->valueCallableIsSafe($value_callable)) {
1250
            $element['#value'] = call_user_func_array($value_callable, [&$element, $input, &$form_state]);
1251 1252 1253 1254
          }
          else {
            $input = NULL;
          }
1255

1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
          if (!isset($element['#value']) && isset($input)) {
            $element['#value'] = $input;
          }
        }
        // Mark all posted values for validation.
        if (isset($element['#value']) || (!empty($element['#required']))) {
          $element['#needs_validation'] = TRUE;
        }
      }
      // Load defaults.
      if (!isset($element['#value'])) {
        // Call #type_value without a second argument to request default_value
        // handling.
1269
        $element['#value'] = call_user_func_array($value_callable, [&$element, FALSE, &$form_state]);
1270

1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
        // Final catch. If we haven't set a value yet, use the explicit default
        // value. Avoid image buttons (which come with garbage value), so we
        // only get value for the button actually clicked.
        if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
          $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
        }
      }
    }

    // Determine which element (if any) triggered the submission of the form and
    // keep track of all the clickable buttons in the form for
1282 1283
    // \Drupal\Core\Form\FormState::cleanValues(). Enforce the same input
    // processing restrictions as above.
1284 1285 1286
    if ($process_input) {
      // Detect if the element triggered the submission via Ajax.
      if ($this->elementTriggeredScriptedSubmission($element, $form_state)) {
1287
        $form_state->setTriggeringElement($element);
1288 1289 1290 1291 1292 1293 1294 1295
      }

      // If the form was submitted by the browser rather than via Ajax, then it
      // can only have been triggered by a button, and we need to determine
      // which button within the constraints of how browsers provide this
      // information.
      if (!empty($element['#is_button'])) {
        // All buttons in the form need to be tracked for
1296 1297 1298
        // \Drupal\Core\Form\FormState::cleanValues() and for the
        // self::doBuildForm() code that handles a form submission containing no
        // button information in \Drupal::request()->request.
1299
        $buttons = $form_state->getButtons();
1300
        $buttons[] = $element;
1301
        $form_state->setButtons($buttons);
1302
        if ($this->buttonWasClicked($element, $form_state)) {
1303
          $form_state->setTriggeringElement($element);
1304 1305 1306 1307
        }
      }
    }

1308
    // Set the element's value in $form_state->getValues(), but only, if its key
1309
    // does not exist yet (a #value_callback may have already populated it).
1310
    if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) {
1311
      $form_state->setValueForElement($element, $element['#value']);
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
    }
  }

  /**
   * Detects if an element triggered the form submission via Ajax.
   *
   * This detects button or non-button controls that trigger a form submission
   * via Ajax or some other scriptable environment. These environments can set
   * the special input key '_triggering_element_name' to identify the triggering
   * element. If the name alone doesn't identify the element uniquely, the input
   * key '_triggering_element_value' may also be set to require a match on
   * element value. An example where this is needed is if there are several
   * // buttons all named 'op', and only differing in their value.
   */
1326
  protected function elementTriggeredScriptedSubmission($element, FormStateInterface &$form_state) {
1327 1328 1329
    $input = $form_state->getUserInput();
    if (!empty($input['_triggering_element_name']) && $element['#name'] == $input['_triggering_element_name']) {
      if (empty($input['_triggering_element_value']) || $input['_triggering_element_value'] == $element['#value']) {
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Determines if a given button triggered the form submission.
   *
   * This detects button controls that trigger a form submission by being
   * clicked and having the click processed by the browser rather than being
   * captured by JavaScript. Essentially, it detects if the button's name and
   * value are part of the POST data, but with extra code to deal with the
   * convoluted way in which browsers submit data for image button clicks.
   *
   * This does not detect button clicks processed by Ajax (that is done in
   * self::elementTriggeredScriptedSubmission()) and it does not detect form
   * submissions from Internet Explorer in response to an ENTER key pressed in a
   * textfield (self::doBuildForm() has extra code for that).
   *
   * Because this function contains only part of the logic needed to determine
1351
   * $form_state->getTriggeringElement(), it should not be called from anywhere
1352 1353
   * other than within the Form API. Form validation and submit handlers needing
   * to know which button was clicked should get that information from
1354
   * $form_state->getTriggeringElement().
1355
   */
1356
  protected function buttonWasClicked($element, FormStateInterface &$form_state) {
1357 1358 1359 1360
    // First detect normal 'vanilla' button clicks. Traditionally, all standard
    // buttons on a form share the same name (usually 'op'), and the specific
    // return value is used to determine which was clicked. This ONLY works as
    // long as $form['#name'] puts the value at the top level of the tree of
1361
    // \Drupal::request()->request data.
1362
    $input = $form_state->getUserInput();
1363 1364 1365 1366 1367
    // The input value attribute is treated as CDATA by browsers. This means
    // that they replace character entities with characters. Therefore, we need
    // to decode the value in $element['#value']. For more details see
    // http://www.w3.org/TR/html401/types.html#type-cdata.
    if (isset($input[$element['#name']]) && $input[$element['#name']] == Html::decodeEntities($element['#value'])) {
1368 1369 1370
      return TRUE;
    }
    // When image buttons are clicked, browsers do NOT pass the form element
1371 1372 1373 1374
    // value in \Drupal::request()->Request. Instead they pass an integer
    // representing the coordinates of the click on the button image. This means
    // that image buttons MUST have unique $form['#name'] values, but the
    // details of their \Drupal::request()->request data should be ignored.
1375 1376 1377 1378 1379 1380
    elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
      return TRUE;
    }
    return FALSE;
  }

1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
  /**
   * Wraps file_upload_max_size().
   *
   * @return string
   *   A translated string representation of the size of the file size limit
   *   based on the PHP upload_max_filesize and post_max_size.
   */
  protected function getFileUploadMaxSize() {
    return file_upload_max_size();
  }

1392 1393 1394 1395 1396 1397
  /**
   * Gets the current active user.
   *
   * @return \Drupal\Core\Session\AccountInterface
   */
  protected function currentUser() {
1398 1399
    if (!$this->currentUser && \Drupal::hasService('current_user')) {
      $this->currentUser = \Drupal::currentUser();
1400 1401 1402 1403 1404
    }
    return $this->currentUser;
  }

}