diff --git a/includes/form.inc b/includes/form.inc
index 10e68ee..d88b39f 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -1918,6 +1918,14 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
     }
   }
 
+  // The 'required' attribute should only be printed when #required is true.
+  // The presence of a boolean attribute on an element represents the true
+  // value, and the absence of the attribute represents the false value.
+  // http://www.w3.org/TR/html5/common-microsyntaxes.html#boolean-attribute.
+  if (!empty($element['#required'])) {
+    $element['#attributes']['required'] = 'required';
+  }
+
   // 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
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index 4d96929..a1f1ace 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -366,6 +366,29 @@ class FormsTestCase extends DrupalWebTestCase {
     $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit'));
     $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.'));
   }
+  
+  /**
+   * Testsrequired attribute.
+   */
+  function testRequiredAttribute() {
+    $this->drupalGet('form-test/required-attribute');
+    $expected = 'required';
+    // Test to make sure the elements have the proper required attribute.
+    foreach (array('textfield', 'password') as $type) {
+      $element = $this->xpath('//input[@id=:id and @required=:expected]', array(
+        ':id' => 'edit-' . $type,
+        ':expected' => $expected,
+      ));
+      $this->assertTrue(!empty($element), t('The @type has the proper required attribute.', array('@type' => $type)));
+    }
+
+    // Test to make sure textarea has the proper required attribute.
+    $element = $this->xpath('//textarea[@id=:id and @required=:expected]', array(
+      ':id' => 'edit-textarea',
+      ':expected' => $expected,
+    ));
+    $this->assertTrue(!empty($element), t('Placeholder text placed in textarea.'));
+  }
 }
 
 /**
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 00be7d2..aca423f 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -195,6 +195,13 @@ function form_test_menu() {
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  
+  $items['form-test/required-attribute'] = array(
+    'title' => 'Required',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_required_attribute'),
+    'access callback' => TRUE,
+  );
 
   return $items;
 }
@@ -1625,3 +1632,18 @@ function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) {
 function _form_test_checkboxes_zero_no_redirect($form, &$form_state) {
   $form_state['redirect'] = FALSE;
 }
+
+/**
+ * Builds a form to test the placeholder attribute.
+ */
+function form_test_required_attribute($form, &$form_state) {
+  foreach (array('textfield', 'textarea', 'password') as $type) {
+    $form[$type] = array(
+      '#type' => $type,
+      '#required' => TRUE,
+      '#title' => $type,
+    );
+  }
+
+  return $form;
+}
