Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1264
diff -u -p -r1.1264 common.inc
--- includes/common.inc	22 Nov 2010 05:22:43 -0000	1.1264
+++ includes/common.inc	25 Nov 2010 02:18:11 -0000
@@ -3687,6 +3687,7 @@ function drupal_html_id($id) {
   }
   $seen_ids = &drupal_static(__FUNCTION__, $seen_ids_init);
 
+  $original_id = $id;
   $id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
 
   // As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
@@ -3711,6 +3712,8 @@ function drupal_html_id($id) {
     $seen_ids[$id] = 1;
   }
 
+  // Allow modules to alter the ID.
+  drupal_alter('html_id', $id, $original_id, $seen_ids);
   return $id;
 }
 
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.216
diff -u -p -r1.216 system.api.php
--- modules/system/system.api.php	23 Nov 2010 03:20:17 -0000	1.216
+++ modules/system/system.api.php	25 Nov 2010 02:18:15 -0000
@@ -804,6 +804,37 @@ function hook_css_alter(&$css) {
 }
 
 /**
+ * Alters an HTML ID before it is output on the page.
+ *
+ * @param $id
+ *   The ID as cleaned and made unique by drupal_html_id().
+ * @param $original_id
+ *   The ID passed to drupal_html_id().
+ * @param $seen_ids
+ *   A keyed array used by drupal_html_id() to implement uniqueness. The keys
+ *   are the cleaned IDs without any counter appended. The value for each item
+ *   is the number of times the base clean ID has been used on this page. This
+ *   parameter does not need to be modified unless the hook implementation wants
+ *   to affect the internal logic of drupal_html_id() the next time that it is
+ *   called.
+ *
+ * @see drupal_html_id()
+ */
+function hook_html_id_alter(&$id, $original_id, $seen_ids) {
+  // If implementing an AJAX GET request, we cannot rely on
+  // $_POST['ajax_html_ids'] containing the list of already used HTML IDs on the
+  // base page, and due to URL length limitations, we cannot seed this variable
+  // directly from other information in the request. Instead, we ensure unique
+  // IDs by prefixing all of them with a client provided salt.
+  $html_id_prefix = &drupal_static(__FUNCTION__);
+  if (!isset($html_id_prefix)) {
+    // Always sanitize client provided data.
+    $html_id_prefix = isset($_GET['html_id_prefix']) ? preg_replace('/[^A-Za-z0-9\-_]/', '', $_GET['html_id_prefix']) : '';
+  }
+  $id = $html_id_prefix . $id;
+}
+
+/**
  * Alter the commands that are sent to the user through the AJAX framework.
  *
  * @param $commands
@@ -811,7 +842,7 @@ function hook_css_alter(&$css) {
  *
  * @see ajax_render()
  */
-function hook_ajax_render_alter($commands) {
+function hook_ajax_render_alter(&$commands) {
   // Inject any new status messages into the content area.
   $commands[] = ajax_command_prepend('#block-system-main .content', theme('status_messages'));
 }
