diff --git a/core/includes/common.inc b/core/includes/common.inc
index 5ff6167..2d1eb70 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -7027,6 +7027,9 @@ function drupal_common_theme() {
     'number' => array(
       'render element' => 'element',
     ),
+    'range' => array(
+      'render element' => 'element',
+    ),
     'form' => array(
       'render element' => 'element',
     ),
diff --git a/core/includes/form.inc b/core/includes/form.inc
index ae04144..61903b6 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -3940,6 +3940,29 @@ function theme_number($variables) {
 }
 
 /**
+ * Returns HTML for a range form element.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #description, #size, #min, #max,
+ *     #required, #attributes, #step.
+ *
+ * @ingroup themeable
+ */
+function theme_range($variables) {
+  $element = $variables['element'];
+
+  $element['#attributes']['type'] = 'range';
+  element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max'));
+  _form_set_class($element, array('form-range'));
+
+  $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
+
+  return $output;
+}
+
+/**
  * Form element validation handler for #type 'number'.
  *
  * Note that #required is validated by _form_validate() already.
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 7163739..5d868a2 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -2267,6 +2267,7 @@ class DrupalWebTestCase extends DrupalTestCase {
           case 'textarea':
           case 'url':
           case 'number':
+          case 'range':
           case 'hidden':
           case 'password':
           case 'email':
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index a3594b9..40586fa 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -413,6 +413,18 @@ function system_element_info() {
     '#theme' => 'number',
     '#theme_wrappers' => array('form_element'),
   );
+  $types['range'] = array(
+    '#input' => TRUE,
+    '#size' => 30,
+    '#step' => 1,
+    '#min' => 0,
+    '#max' => 100,
+    '#maxlength' => 128,
+    '#process' => array('ajax_process_form'),
+    '#element_validate' => array('form_validate_number'),
+    '#theme' => 'range',
+    '#theme_wrappers' => array('form_element'),
+  );
   $types['machine_name'] = array(
     '#input' => TRUE,
     '#default_value' => NULL,
diff --git a/core/modules/system/tests/form.test b/core/modules/system/tests/form.test
index eed1287..946da0b 100644
--- a/core/modules/system/tests/form.test
+++ b/core/modules/system/tests/form.test
@@ -301,12 +301,11 @@ class FormsTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Tests validation of #type 'number' elements.
+   * Tests validation of #type 'number' and 'range' elements.
    */
   function testNumber() {
     $form = $form_state = array();
     $form = form_test_number($form, $form_state);
-    $this->drupalGet('form-test/number');
 
     // Array with all the error messages to be checked.
     $error_messages = array(
@@ -339,25 +338,30 @@ class FormsTestCase extends DrupalWebTestCase {
       'float_step_any_no_error' => 0,
     );
 
-    // Post form and show errors.
-    $this->drupalPost(NULL, array(), 'Submit');
-
-    foreach ($expected as $element => $error) {
-      // Create placeholder array.
-      $placeholders = array(
-        '%name' => $form[$element]['#title'],
-        '%min' => isset($form[$element]['#min']) ? $form[$element]['#min'] : '0',
-        '%max' => isset($form[$element]['#max']) ? $form[$element]['#max'] : '0',
-      );
-
-      foreach ($error_messages as $id => $message) {
-        // Check if the error exists on the page, if the current message ID is
-        // expected. Otherwise ensure that the error message is not present.
-        if ($id === $error) {
-          $this->assertRaw(format_string($message, $placeholders));
-        }
-        else {
-          $this->assertNoRaw(format_string($message, $placeholders));
+    // First test the number element type, then range.
+    foreach (array('form-test/number', 'form-test/number/range') as $path) {
+      $this->drupalGet($path);
+
+      // Post form and show errors.
+      $this->drupalPost(NULL, array(), 'Submit');
+
+      foreach ($expected as $element => $error) {
+        // Create placeholder array.
+        $placeholders = array(
+          '%name' => $form[$element]['#title'],
+          '%min' => isset($form[$element]['#min']) ? $form[$element]['#min'] : '0',
+          '%max' => isset($form[$element]['#max']) ? $form[$element]['#max'] : '0',
+        );
+
+        foreach ($error_messages as $id => $message) {
+          // Check if the error exists on the page, if the current message ID is
+          // expected. Otherwise ensure that the error message is not present.
+          if ($id === $error) {
+            $this->assertRaw(format_string($message, $placeholders));
+          }
+          else {
+            $this->assertNoRaw(format_string($message, $placeholders));
+          }
         }
       }
     }
@@ -406,7 +410,7 @@ class FormsTestCase extends DrupalWebTestCase {
 
     // All the elements should be marked as disabled, including the ones below
     // the disabled container.
-    $this->assertEqual(count($disabled_elements), 38, 'The correct elements have the disabled property in the HTML code.');
+    $this->assertEqual(count($disabled_elements), 39, 'The correct elements have the disabled property in the HTML code.');
 
     $this->drupalPost(NULL, $edit, t('Submit'));
     $returned_values['hijacked'] = drupal_json_decode($this->content);
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index a2a2815..c727c51 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -139,6 +139,12 @@ function form_test_menu() {
     'page arguments' => array('form_test_number'),
     'access callback' => TRUE,
   );
+  $items['form-test/number/range'] = array(
+    'title' => 'Range',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_number', 'range'),
+    'access callback' => TRUE,
+  );
   $items['form-test/checkboxes-radios'] = array(
     'title' => t('Checkboxes, Radios'),
     'page callback' => 'drupal_get_form',
@@ -1153,11 +1159,14 @@ function form_test_select_submit($form, &$form_state) {
 }
 
 /**
- * Builds a form to test #type 'number' validation.
+ * Builds a form to test #type 'number' and 'range' validation.
+ *
+ * @param $element
+ *   The element type to test. Can be 'number' or 'range'. Defaults to 'number'.
  */
-function form_test_number($form, &$form_state) {
+function form_test_number($form, &$form_state, $element = 'number') {
   $base = array(
-    '#type' => 'number',
+    '#type' => $element,
   );
 
   $form['integer_no_number'] = $base + array(
@@ -1476,23 +1485,16 @@ function _form_test_disabled_elements($form, &$form_state) {
     );
   }
 
-  // Weight.
-  $form['weight'] = array(
-    '#type' => 'weight',
-    '#title' => 'weight',
-    '#default_value' => 10,
-    '#test_hijack_value' => 5,
-    '#disabled' => TRUE,
-  );
-
-  // Number.
-  $form['number'] = array(
-    '#type' => 'number',
-    '#title' => 'number',
-    '#disabled' => TRUE,
-    '#default_value' => 1,
-    '#test_hijack_value' => 2,
-  );
+  // Weight, number, range.
+  foreach (array('weight', 'number', 'range') as $type) {
+    $form[$type] = array(
+      '#type' => $type,
+      '#title' => $type,
+      '#default_value' => 10,
+      '#test_hijack_value' => 5,
+      '#disabled' => TRUE,
+    );
+  }
 
   // Date.
   $form['date'] = array(
