diff --git a/includes/ajax.inc b/includes/ajax.inc
index f059209..ddcf890 100644
--- a/includes/ajax.inc
+++ b/includes/ajax.inc
@@ -641,6 +641,8 @@ function ajax_footer() {
  * @see ajax_pre_render_element()
  */
 function ajax_process_form($element, &$form_state) {
+  $element['#attached']['library'][] = array('system', 'drupal.form');
+
   $element = ajax_pre_render_element($element);
   if (!empty($element['#ajax_processed'])) {
     $form_state['cache'] = TRUE;
diff --git a/misc/form.js b/misc/form.js
index 259b84e..ada8f72 100644
--- a/misc/form.js
+++ b/misc/form.js
@@ -58,6 +58,64 @@ Drupal.behaviors.formUpdated = {
 };
 
 /**
+ * Prevents consecutive form submissions of identical form values.
+ *
+ * Repetitive form submissions that would submit the identical form values are
+ * prevented, unless the form values are different to the previously submitted
+ * values.
+ *
+ * This is a simplified re-implementation of a user-agent behavior that should
+ * be natively supported by major web browsers, but at this time, only Firefox
+ * has a built-in protection.
+ *
+ * A form value-based approach ensures that the constraint is triggered for
+ * consecutive, identical form submissions only. Compared to that, a form
+ * button-based approach would (1) rely on [visible] buttons to exist where
+ * technically not required and (2) require more complex state management if
+ * there are multiple buttons in a form.
+ *
+ * This implementation is based on form-level submit events only and relies on
+ * jQuery's serialize() method to determine submitted form values. As such, the
+ * following limitations exist:
+ *
+ * - Event handlers on form buttons that preventDefault() do not receive a
+ *   double-submit protection. That is deemed to be fine, since such button
+ *   events typically trigger reversible client-side or server-side operations
+ *   that are local to the context of a form only.
+ * - Changed values in advanced form controls, such as file inputs, are not part
+ *   of the form values being compared between consecutive form submits (due to
+ *   limitations of jQuery.serialize()). That is deemed to be acceptable,
+ *   because if the user forgot to attach a file, then the size of HTTP payload
+ *   will most likely be small enough to be fully passed to the server endpoint
+ *   within (milli)seconds. If a user mistakenly attached a wrong file and is
+ *   technically versed enough to cancel the form submission (and HTTP payload)
+ *   in order to attach a different file, then that edge-case is not supported
+ *   here.
+ *
+ * Lastly, all forms submitted via HTTP GET are idempotent by definition of HTTP
+ * standards, so excluded in this implementation.
+ */
+Drupal.behaviors.formSingleSubmit = {
+  attach: function () {
+    function onFormSubmit (e) {
+      var $form = $(e.currentTarget);
+      var formValues = $form.serialize();
+      var previousValues = $form.attr('data-drupal-form-submit-last');
+      if (previousValues === formValues) {
+        e.preventDefault();
+      }
+      else {
+        $form.attr('data-drupal-form-submit-last', formValues);
+      }
+    }
+
+    $('body').once('form-single-submit')
+      .delegate('form:not([method~="GET"])', 'submit.singleSubmit', onFormSubmit);
+
+  }
+};
+
+/**
  * Prepopulate form fields with information from the visitor cookie.
  */
 Drupal.behaviors.fillUserInfoFromCookie = {
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index 92aefe4..7a6571d 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -1521,17 +1521,6 @@ class JavaScriptTestCase extends DrupalWebTestCase {
     $this->assertRaw('Drupal.settings', 'Default behavior: The front page of the site includes Drupal settings.');
     $this->assertRaw('basePath', 'Default behavior: The front page of the site includes the basePath Drupal setting.');
 
-    // The default front page should not use jQuery and other standard scripts
-    // and settings when the 'javascript_always_use_jquery' variable is set to
-    // FALSE.
-    variable_set('javascript_always_use_jquery', FALSE);
-    $this->drupalGet('');
-    $this->assertNoRaw('misc/jquery.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include jquery.js.');
-    $this->assertNoRaw('misc/drupal.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include drupal.js.');
-    $this->assertNoRaw('Drupal.settings', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings.');
-    $this->assertNoRaw('basePath', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include the basePath Drupal setting.');
-    variable_del('javascript_always_use_jquery');
-
     // When only settings have been added via drupal_add_js(), drupal_get_js()
     // should still return jQuery and other standard scripts and settings.
     $this->resetStaticVariables();
