Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.506
diff -u -p -r1.506 form.inc
--- includes/form.inc	21 Oct 2010 20:46:58 -0000	1.506
+++ includes/form.inc	26 Oct 2010 00:27:23 -0000
@@ -2072,14 +2072,35 @@ function form_type_image_button_value($f
  *   The data that will appear in the $element_state['values'] collection
  *   for this element. Return nothing to use the default.
  */
-function form_type_checkbox_value($element, $input = FALSE) {
+function form_type_checkbox_value(&$element, $input = FALSE) {
+  // On form submission, the #value of
+  // - a checked checkbox element is #return_value.
+  // - an unchecked checkbox element is FALSE.
   if ($input !== FALSE) {
     // Successful (checked) checkboxes are present with a value (possibly '0').
     // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
     // For an unchecked checkbox, we return integer 0, so we can explicitly
     // test for a value different than string '0'.
-    return isset($input) ? $element['#return_value'] : 0;
+    $value = isset($input) ? $element['#return_value'] : FALSE;
   }
+  // On not submitted forms, the #value of a checkbox is #default_value, which
+  // form_builder() sets to FALSE by default.
+  else {
+    $value = $element['#default_value'];
+  }
+  // Most of the time, a relaxed equals check is adequate so that a #value of
+  // '1' matches #return_value 1. The isset in form_process_checkboxes() is
+  // type agnostic too so that won't interfere with this. There are a few
+  // exceptions:
+  // - TRUE means checked.
+  // - FALSE, NULL and 0 simply means unchecked.
+  if ($value === TRUE || $value === FALSE || $value === 0 || $value === NULL) {
+    $element['#checked'] = (bool) $value;
+  }
+  else {
+    $element['#checked'] = ($value == $element['#return_value']);
+  }
+  return $value;
 }
 
 /**
@@ -2109,7 +2130,8 @@ function form_type_checkboxes_value($ele
     // NULL elements from the array before constructing the return value, to
     // simulate the behavior of web browsers (which do not send unchecked
     // checkboxes to the server at all). This will not affect non-programmatic
-    // form submissions, since a checkbox can never legitimately be NULL.
+    // form submissions, since all values in $_POST are strings so they can
+    // never legitimately be NULL.
     foreach ($input as $key => $value) {
       if (!isset($value)) {
         unset($input[$key]);
@@ -2804,10 +2826,10 @@ function theme_checkbox($variables) {
   $element = $variables['element'];
   $t = get_t();
   $element['#attributes']['type'] = 'checkbox';
+  // #return_value ends up as the HTML attribute 'value', so sanitize it.
+  $element['#return_value'] = check_plain($element['#return_value']);
   element_set_attributes($element, array('id', 'name', '#return_value' => 'value'));
-
-  // Unchecked checkbox has #value of integer 0.
-  if (isset($element['#return_value']) && isset($element['#value']) && $element['#value'] !== 0 && $element['#value'] == $element['#return_value']) {
+  if ($element['#checked']) {
     $element['#attributes']['checked'] = 'checked';
   }
   _form_set_class($element, array('form-checkbox'));
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.130
diff -u -p -r1.130 common.test
--- modules/simpletest/tests/common.test	8 Oct 2010 15:36:12 -0000	1.130
+++ modules/simpletest/tests/common.test	25 Oct 2010 23:45:35 -0000
@@ -1533,6 +1533,7 @@ class DrupalRenderTestCase extends Drupa
 
     $element = array(
       '#type' => 'checkbox',
+      '#value' => TRUE,
       '#title' => $this->randomName(),
     );
     $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'checkbox'));
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.72
diff -u -p -r1.72 form.test
--- modules/simpletest/tests/form.test	4 Oct 2010 18:00:46 -0000	1.72
+++ modules/simpletest/tests/form.test	26 Oct 2010 00:20:44 -0000
@@ -1352,3 +1352,53 @@ class FormsFileInclusionTestCase extends
     $this->assertText('Submit callback called.');
   }
 }
+
+/**
+ * Test checkbox element.
+ */
+class FormCheckboxTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Checkbox',
+      'description' => 'Tests form API checkbox handling especially 0, empty string etc.',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  function testFormCheckbox() {
+    $default_value_array = array(0, FALSE, NULL, TRUE, '0', '', 1, '1', 'foobar');
+    $return_value_array = array('0', '', 1, '1', 'foobar');
+    foreach ($default_value_array as $default_value_key => $default_value) {
+      foreach ($return_value_array as $return_value) {
+        $form_array = drupal_get_form('form_test_checkbox_type_juggling', $default_value, $return_value);
+        $form = drupal_render($form_array);
+        if ($default_value_key < 3) {
+          $checked = FALSE;
+        }
+        elseif ($default_value_key == 3) {
+          $checked = TRUE;
+        }
+        else {
+          if ($return_value === '0' || $return_value === '') {
+            $checked = $default_value === $return_value;
+          }
+          else {
+            $checked = $default_value == $return_value;
+          }
+        }
+        $checked_in_html = (strpos($form, 'checked') !== FALSE);
+        $message = t('#default_value is @default_value, #return_value is @return_value.', array(
+          '@default_value' => var_export($default_value, TRUE),
+          '@return_value' => var_export($return_value, TRUE),
+        ));
+        $this->assertIdentical($checked, $checked_in_html, $message);
+      }
+    }
+  }
+}
Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.52
diff -u -p -r1.52 form_test.module
--- modules/simpletest/tests/form_test.module	20 Oct 2010 01:15:58 -0000	1.52
+++ modules/simpletest/tests/form_test.module	25 Oct 2010 23:45:35 -0000
@@ -1395,3 +1395,12 @@ function form_test_load_include_custom($
   $form_state['cache'] = TRUE;
   return $form;
 }
+
+function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) {
+  $form['checkbox'] = array(
+    '#type' => 'checkbox',
+    '#return_value' => $return_value,
+    '#default_value' => $default_value,
+  );
+  return $form;
+}
