Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.493
diff -u -p -r1.493 form.inc
--- includes/form.inc	16 Sep 2010 20:14:49 -0000	1.493
+++ includes/form.inc	19 Sep 2010 03:22:43 -0000
@@ -986,13 +986,11 @@ function drupal_validate_form($form_id, 
 
   // If the session token was set by drupal_prepare_form(), ensure that it
   // matches the current user's session.
-  if (isset($form['#token'])) {
-    if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
-      // Setting this error will cause the form to fail validation.
-      form_set_error('form_token', t('This form is outdated. Reload the page and try again. Contact the site administrator if the problem persists.'));
-    }
+  if (isset($form['#token']) && !drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
+    // Setting this error will cause the form to fail validation.
+    form_set_error('form_token', t('This form was outdated and has been reloaded automatically. Try to submit it again. Contact the site administrator if the problem persists.'));
+    $reset_token = TRUE;
   }
-
   _form_validate($form, $form_state, $form_id);
   $validated_forms[$form_id] = TRUE;
 
@@ -1016,6 +1014,15 @@ function drupal_validate_form($form_id, 
     }
     $form_state['values'] = $values;
   }
+
+  // If the session token did not match the current user's session, reset the
+  // token, so the user is able to submit the form by submitting it again.
+  if (isset($reset_token)) {
+    // Setting this value will cause the form to have an up-to-date token
+    // when it is re-rendered, therefore allowing the new copy of the form
+    // to be submitted successfully.
+    $form['form_token']['#value'] = drupal_get_token($form['#token']);
+  }
 }
 
 /**
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.64
diff -u -p -r1.64 form.test
--- modules/simpletest/tests/form.test	13 Sep 2010 06:41:23 -0000	1.64
+++ modules/simpletest/tests/form.test	19 Sep 2010 04:01:07 -0000
@@ -7,17 +7,24 @@
  */
 
 class FormsTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Form element validation',
+      'name' => 'Element validation',
       'description' => 'Tests various form element validation mechanisms.',
       'group' => 'Form API',
     );
   }
 
   function setUp() {
-    parent::setUp('form_test');
+    parent::setUp('form_test', 'file');
+
+    // Add a text format to get a full text format widget.
+    $format = new stdClass;
+    $format->name = 'Test format 1';
+    filter_format_save($format);
+    user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(filter_permission_name($format)));
   }
 
   /**
@@ -309,16 +316,18 @@ class FormsTestCase extends DrupalWebTes
  * Test form alter hooks.
  */
 class FormAlterTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
   public static function getInfo() {
     return array(
-      'name' => 'Form alter hooks',
+      'name' => 'Alter hooks',
       'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().',
       'group' => 'Form API',
     );
   }
 
   function setUp() {
-    parent::setUp('form_test');
+    parent::setUp('form_test', 'block');
   }
 
   /**
@@ -343,6 +352,8 @@ class FormAlterTestCase extends DrupalWe
  * Test form validation handlers.
  */
 class FormValidationTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
   public static function getInfo() {
     return array(
       'name' => 'Form validation handlers',
@@ -417,16 +428,45 @@ class FormValidationTestCase extends Dru
     $this->assertText(t('!name field is required.', array('!name' => 'Title')));
     $this->assertText('Test element is invalid');
   }
+
+  /**
+   * Tests re-submitting a form that contains an expired session token.
+   */
+  function testExpiredToken() {
+    $this->web_user = $this->drupalCreateUser(array());
+
+    $this->drupalLogin($this->web_user);
+    $this->drupalGet('form-test/validate');
+    $copy = $this->drupalGetContent();
+    $url = $this->getUrl();
+    $this->drupalLogout();
+
+    $this->drupalLogin($this->web_user);
+    $this->drupalSetContent($copy, $url);
+
+    $edit = array(
+      'name' => 'Drupal',
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertText(t('This form was outdated and has been reloaded automatically. Try to submit it again. Contact the site administrator if the problem persists.'));
+    $this->assertNoText(t('@label value: @value', array('@label' => 'Name', '@value' => $edit['name'])));
+
+    // Do what it suggests us to do.
+    $this->drupalPost(NULL, array(), 'Save');
+    $this->assertNoText(t('This form was outdated and has been reloaded automatically. Try to submit it again. Contact the site administrator if the problem persists.'));
+    $this->assertText(t('@label value: @value', array('@label' => 'Name', '@value' => $edit['name'])));
+  }
 }
 
 /**
  * Test form element labels, required markers and associated output.
  */
 class FormsElementsLabelsTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Form element and label output test',
+      'name' => 'Labels and markers',
       'description' => 'Test form element labels, required markers and associated output.',
       'group' => 'Form API',
     );
@@ -496,10 +536,11 @@ class FormsElementsLabelsTestCase extend
  * Test the tableselect form element for expected behavior.
  */
 class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Tableselect form element type test',
+      'name' => 'Tableselect',
       'description' => 'Test the tableselect element for expected behavior',
       'group' => 'Form API',
     );
@@ -509,14 +550,11 @@ class FormsElementsTableSelectFunctional
     parent::setUp('form_test');
   }
 
-
   /**
    * Test the display of checkboxes when #multiple is TRUE.
    */
   function testMultipleTrue() {
-
     $this->drupalGet('form_test/tableselect/multiple-true');
-
     $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
 
     // Test for the presence of the Select all rows tableheader.
@@ -533,7 +571,6 @@ class FormsElementsTableSelectFunctional
    */
   function testMultipleFalse() {
     $this->drupalGet('form_test/tableselect/multiple-false');
-
     $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.'));
 
     // Test for the absence of the Select all rows tableheader.
@@ -557,9 +594,7 @@ class FormsElementsTableSelectFunctional
    * Test the submission of single and multiple values when #multiple is TRUE.
    */
   function testMultipleTrueSubmit() {
-
     // Test a submission with one checkbox checked.
-    $edit = array();
     $edit['tableselect[row1]'] = TRUE;
     $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit');
 
@@ -575,7 +610,6 @@ class FormsElementsTableSelectFunctional
     $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1.'));
     $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.'));
     $this->assertText(t('Submitted: row3 = row3'), t('Checked checkbox row3.'));
-
   }
 
   /**
@@ -607,14 +641,11 @@ class FormsElementsTableSelectFunctional
     $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.'));
   }
 
-
   /**
    * Test the whether the option checker gives an error on invalid tableselect values for checkboxes.
    */
   function testMultipleTrueOptionchecker() {
-
     list($header, $options) = _form_test_tableselect_get_data();
-
     $form['tableselect'] = array(
       '#type' => 'tableselect',
       '#header' => $header,
@@ -631,14 +662,11 @@ class FormsElementsTableSelectFunctional
 
   }
 
-
   /**
    * Test the whether the option checker gives an error on invalid tableselect values for radios.
    */
   function testMultipleFalseOptionchecker() {
-
     list($header, $options) = _form_test_tableselect_get_data();
-
     $form['tableselect'] = array(
       '#type' => 'tableselect',
       '#header' => $header,
@@ -655,7 +683,6 @@ class FormsElementsTableSelectFunctional
     $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for radio buttons.'));
   }
 
-
   /**
    * Helper function for the option check test to submit a form while collecting errors.
    *
@@ -677,9 +704,7 @@ class FormsElementsTableSelectFunctional
     $form_state['input']['form_id'] = $form_id;
 
     drupal_prepare_form($form_id, $form, $form_state);
-
     drupal_process_form($form_id, $form, $form_state);
-
     $errors = form_get_errors();
 
     // Clear errors and messages.
@@ -690,7 +715,6 @@ class FormsElementsTableSelectFunctional
     // to allow the caller lowlevel access to the form.
     return array($form, $form_state, $errors);
   }
-
 }
 
 /**
@@ -703,11 +727,12 @@ class FormsElementsTableSelectFunctional
  * values aren't lost due to a wrong form rebuild.
  */
 class FormsFormStorageTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name'  => 'Multistep form using form storage',
-      'description'  => 'Tests a multistep form using form storage and makes sure validation and caching works right.',
+      'name' => 'Multistep form storage',
+      'description' => 'Tests a multistep form using form storage and makes sure validation and caching works right.',
       'group' => 'Form API',
     );
   }
@@ -853,9 +878,11 @@ class FormsFormStorageTestCase extends D
  * Test wrapper form callbacks.
  */
 class FormsFormWrapperTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
   public static function getInfo() {
     return array(
-      'name' => 'Form wrapper callback',
+      'name' => 'Wrapper callback',
       'description' => 'Tests form wrapper callbacks to pass a prebuilt form to form builder functions.',
       'group' => 'Form API',
     );
@@ -879,6 +906,8 @@ class FormsFormWrapperTestCase extends D
  * Test $form_state clearance.
  */
 class FormStateValuesCleanTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
   public static function getInfo() {
     return array(
       'name' => 'Form state values clearance',
@@ -931,7 +960,7 @@ class FormStateValuesCleanTestCase exten
 class FormsRebuildTestCase extends DrupalWebTestCase {
   public static function getInfo() {
     return array(
-      'name' => 'Form rebuilding',
+      'name' => 'Rebuild',
       'description' => 'Tests functionality of drupal_rebuild_form().',
       'group' => 'Form API',
     );
@@ -1025,10 +1054,11 @@ class FormsRebuildTestCase extends Drupa
  * Test the programmatic form submission behavior.
  */
 class FormsProgrammaticTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Programmatic form submissions',
+      'name' => 'Programmed submissions',
       'description' => 'Test the programmatic form submission behavior.',
       'group' => 'Form API',
     );
@@ -1105,10 +1135,11 @@ class FormsProgrammaticTestCase extends 
  * Test that FAPI correctly determines $form_state['triggering_element'].
  */
 class FormsTriggeringElementTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Form triggering element determination',
+      'name' => 'Triggering element',
       'description' => 'Test the determination of $form_state[\'triggering_element\'].',
       'group' => 'Form API',
     );
@@ -1269,10 +1300,11 @@ class FormsArbitraryRebuildTestCase exte
  * Tests form API file inclusion.
  */
 class FormsFileInclusionTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
 
   public static function getInfo() {
     return array(
-      'name' => 'Form API file inclusion',
+      'name' => 'Include files',
       'description' => 'Tests form API file inclusion.',
       'group' => 'Form API',
     );
@@ -1283,17 +1315,14 @@ class FormsFileInclusionTestCase extends
   }
 
   /**
-   * Tests loading an include specified in hook_menu().
+   * Tests form state file inclusion.
    */
   function testLoadMenuInclude() {
+    // Verify loading an include specified in hook_menu().
     $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu');
     $this->assertText('Submit callback called.');
-  }
 
-  /**
-   * Tests loading a custom specified inlcude.
-   */
-  function testLoadCustomInclude() {
+    // Verify loading a custom specified include.
     $this->drupalPost('form-test/load-include-custom', array(), t('Save'));
     $this->assertText('Submit callback called.');
   }
Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.48
diff -u -p -r1.48 form_test.module
--- modules/simpletest/tests/form_test.module	27 Aug 2010 11:54:32 -0000	1.48
+++ modules/simpletest/tests/form_test.module	19 Sep 2010 03:05:26 -0000
@@ -298,6 +298,17 @@ function form_test_validate_form_validat
 }
 
 /**
+ * Form submit handler for form_test_validate_form().
+ */
+function form_test_validate_form_submit(&$form, &$form_state) {
+  // Output the element's value from $form_state.
+  drupal_set_message(t('@label value: @value', array(
+    '@label' => $form['name']['#title'],
+    '@value' => $form_state['values']['name'],
+  )));
+}
+
+/**
  * Builds a simple form with a button triggering partial validation.
  */
 function form_test_limit_validation_errors_form($form, &$form_state) {
