Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.663
diff -u -r1.663 system.module
--- modules/system/system.module	31 Jan 2009 15:55:05 -0000	1.663
+++ modules/system/system.module	1 Feb 2009 04:54:25 -0000
@@ -408,6 +408,12 @@
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  $items['system/ahah'] = array(
+    'title' => 'AHAH callback',
+    'page callback' => 'form_ahah_callback',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
   $items['system/timezone'] = array(
     'title' => 'Time zone',
     'page callback' => 'system_timezone',
Index: modules/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.286
diff -u -r1.286 poll.module
--- modules/poll/poll.module	22 Jan 2009 12:46:06 -0000	1.286
+++ modules/poll/poll.module	1 Feb 2009 04:54:25 -0000
@@ -118,13 +118,6 @@
     'type' => MENU_LOCAL_TASK,
   );
 
-  $items['poll/js'] = array(
-    'title' => 'Javascript Choice Form',
-    'page callback' => 'poll_choice_js',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-
   return $items;
 }
 
@@ -266,7 +259,7 @@
     '#weight' => 1,
     '#submit' => array('poll_more_choices_submit'), // If no javascript action.
     '#ahah' => array(
-      'path' => 'poll/js',
+      'callback' => 'poll_choice_js',
       'wrapper' => 'poll-choices',
       'method' => 'replace',
       'effect' => 'fade',
@@ -315,7 +308,7 @@
 
   // Make the changes we want to the form state.
   if ($form_state['values']['poll_more']) {
-    $n = $_GET['q'] == 'poll/js' ? 1 : 5;
+    $n = $_GET['q'] == 'system/ahah' ? 1 : 5;
     $form_state['choice_count'] = count($form_state['values']['choice']) + $n;
   }
 }
@@ -361,36 +354,13 @@
 }
 
 /**
- * Menu callback for AHAH additions.
+ * Menu callback for AHAH additions. Render the new poll choices.
  */
-function poll_choice_js() {
-  $form_state = array('storage' => NULL, 'submitted' => FALSE);
-  $form_build_id = $_POST['form_build_id'];
-
-  // Get the form from the cache.
-  $form = form_get_cache($form_build_id, $form_state);
-  $args = $form['#parameters'];
-  $form_id = array_shift($args);
-
-  // We will run some of the submit handlers so we need to disable redirecting.
-  $form['#redirect'] = FALSE;
-
-  // We need to process the form, prepare for that by setting a few internals
-  // variables.
-  $form['#post'] = $_POST;
-  $form['#programmed'] = FALSE;
-  $form_state['post'] = $_POST;
-
-  // Build, validate and if possible, submit the form.
-  drupal_process_form($form_id, $form, $form_state);
-
-  // This call recreates the form relying solely on the form_state that the
-  // drupal_process_form set up.
-  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
-
-  // Render the new output.
+function poll_choice_js($form, $form_state) {
   $choice_form = $form['choice_wrapper']['choice'];
-  unset($choice_form['#prefix'], $choice_form['#suffix']); // Prevent duplicate wrappers.
+
+  // Prevent duplicate wrappers.
+  unset($choice_form['#prefix'], $choice_form['#suffix']);
   $output = theme('status_messages') . drupal_render($choice_form);
 
   drupal_json(array('status' => TRUE, 'data' => $output));
Index: modules/poll/poll.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.test,v
retrieving revision 1.14
diff -u -r1.14 poll.test
--- modules/poll/poll.test	18 Dec 2008 14:38:37 -0000	1.14
+++ modules/poll/poll.test	1 Feb 2009 04:54:25 -0000
@@ -187,7 +187,7 @@
     // @TODO: the framework should make it possible to submit a form to a
     // different URL than its action or the current. For now, we can just force
     // it.
-    $this->additionalCurlOptions[CURLOPT_URL] = url('poll/js', array('absolute' => TRUE));
+    $this->additionalCurlOptions[CURLOPT_URL] = url('system/ahah', array('absolute' => TRUE));
     $this->drupalPost(NULL, $edit, t('More choices'));
     unset($this->additionalCurlOptions[CURLOPT_URL]);
 
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.318
diff -u -r1.318 form.inc
--- includes/form.inc	28 Jan 2009 07:43:26 -0000	1.318
+++ includes/form.inc	1 Feb 2009 04:54:25 -0000
@@ -1759,6 +1759,39 @@
 }
 
 /**
+ * Menu callback for AHAH callbacks through the #ahah['callback'] FAPI property.
+ */
+function form_ahah_callback() {
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+
+  // Get the form from the cache.
+  $form = form_get_cache($form_build_id, $form_state);
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+
+  // We will run some of the submit handlers so we need to disable redirecting.
+  $form['#redirect'] = FALSE;
+
+  // We need to process the form, prepare for that by setting a few internals
+  // variables.
+  $form['#post'] = $_POST;
+  $form['#programmed'] = FALSE;
+  $form_state['post'] = $_POST;
+
+  // Build, validate and if possible, submit the form.
+  drupal_process_form($form_id, $form, $form_state);
+
+  // This call recreates the form relying solely on the form_state that the
+  // drupal_process_form set up.
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+
+  // Get the callback function from the clicked button.
+  $callback = $form_state['clicked_button']['#ahah']['callback'];
+  $callback($form, $form_state);
+}
+
+/**
  * Roll out a single radios element to a list of radios,
  * using the options array as index.
  */
@@ -1875,7 +1908,7 @@
 function form_process_ahah($element) {
   static $js_added = array();
   // Add a reasonable default event handler if none specified.
-  if (isset($element['#ahah']['path']) && !isset($element['#ahah']['event'])) {
+  if (isset($element['#ahah']) && !isset($element['#ahah']['event'])) {
     switch ($element['#type']) {
       case 'submit':
       case 'button':
@@ -1905,12 +1938,12 @@
 
   // Adding the same javascript settings twice will cause a recursion error,
   // we avoid the problem by checking if the javascript has already been added.
-  if (isset($element['#ahah']['path']) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) {
+  if ((isset($element['#ahah']['callback']) || isset($element['#ahah']['path'])) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) {
     drupal_add_js('misc/jquery.form.js', array('weight' => JS_LIBRARY));
     drupal_add_js('misc/ahah.js');
 
     $ahah_binding = array(
-      'url'      => url($element['#ahah']['path']),
+      'url'      => isset($element['#ahah']['callback']) ? url('system/ahah') : url($element['#ahah']['path']),
       'event'    => $element['#ahah']['event'],
       'keypress' => empty($element['#ahah']['keypress']) ? NULL : $element['#ahah']['keypress'],
       'wrapper'  => empty($element['#ahah']['wrapper']) ? NULL : $element['#ahah']['wrapper'],
