? 0
? 443154-fatal-errors_20.patch
? a
? empty-parens-376408-1.patch.1
? es
? foo.php
? handlet-comments.txt
? placeholders-test.php
? pluggable-smtp-331180-56.patch
? pluggable-smtp-331180-58.patch
? r.diff
? registry_rip-token.patch
? registry_rip.patch
? slash.php
? struct-link-short-552478-117a.patch
? test.html
? modules/user/user.password.inc
? sites/all/modules
? sites/default/files
? sites/default/settings.php
Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal/drupal/CHANGELOG.txt,v
retrieving revision 1.345
diff -u -p -r1.345 CHANGELOG.txt
--- CHANGELOG.txt	12 Oct 2009 05:22:56 -0000	1.345
+++ CHANGELOG.txt	16 Oct 2009 00:11:53 -0000
@@ -141,6 +141,12 @@ Drupal 7.0, xxxx-xx-xx (development vers
 - Added RDF support:
     * Modules can declare RDF namespaces which are serialized in the <html> tag
       for RDFa support.
+- Search engine optimization and web linking:
+    * Added a rel="canonical" link on node and comment pages to prevent
+      duplicate content indexing by search engines.
+    * Added a default rel="shortlink" link on node and comment pages that
+      advertises a short link as an alternative URL to third-party services.
+    * Meta information is now alterable by all modules before rendering.
 - Field API:
     * Custom data fields may be attached to nodes, users, comments and taxonomy
       terms.
Index: includes/batch.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/batch.inc,v
retrieving revision 1.42
diff -u -p -r1.42 batch.inc
--- includes/batch.inc	15 Oct 2009 16:18:44 -0000	1.42
+++ includes/batch.inc	16 Oct 2009 00:11:54 -0000
@@ -190,7 +190,14 @@ function _batch_progress_page_nojs() {
   }
 
   $url = url($batch['url'], array('query' => array('id' => $batch['id'], 'op' => $new_op)));
-  drupal_add_html_head('<meta http-equiv="Refresh" content="0; URL=' . $url . '">');
+  $element = array(
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'http-equiv' => 'Refresh',
+      'content' => '0; URL=' . $url,
+    ),
+  );
+  drupal_add_html_head($element, 'batch_progress_meta_refresh');
 
   return theme('progress_bar', array('percent' => $percentage, 'message' => $message));
 }
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1021
diff -u -p -r1.1021 common.inc
--- includes/common.inc	15 Oct 2009 21:19:30 -0000	1.1021
+++ includes/common.inc	16 Oct 2009 00:12:13 -0000
@@ -280,23 +280,78 @@ function drupal_get_rdf_namespaces() {
 /**
  * Add output to the head tag of the HTML page.
  *
- * This function can be called as long the headers aren't sent.
+ * This function can be called as long the headers aren't sent. Pass no
+ * arguments (or NULL for both) to retrieve the currently stored elements.
+ *
+ * @param $data
+ *   A renderable array. If the '#type' key is not set then 'html_tag' will be
+ *   added as the default '#type'.
+ * @param $key
+ *   A unique string key to allow implementations of hook_html_head_alter() to
+ *   identify the element in $data. Required if $data is not NULL.
+ *
+ * @return
+ *   An array of all stored HEAD elements.
+ *
+ * @see theme_html_tag()
  */
-function drupal_add_html_head($data = NULL) {
-  $stored_head = &drupal_static(__FUNCTION__, '');
+function drupal_add_html_head($data = NULL, $key = NULL) {
+  $stored_head = &drupal_static(__FUNCTION__);
 
-  if (!is_null($data)) {
-    $stored_head .= $data . "\n";
+  if (!isset($stored_head)) {
+    // Make sure the defaults, including Content-Type, come first.
+    $stored_head = _drupal_default_html_head();
+  }
+
+  if (isset($data) && isset($key)) {
+    if (!isset($data['#type'])) {
+      $data['#type'] = 'html_tag';
+    }
+    $stored_head[$key] = $data;
   }
   return $stored_head;
 }
 
 /**
- * Retrieve output to be displayed in the head tag of the HTML page.
+ * Returns elements that are always displayed in the HEAD tag of the HTML page.
+ */
+function _drupal_default_html_head() {
+  // Add default elements. Make sure the Content-Type comes first because the
+  // IE browser may be vulnerable to XSS via encoding attacks from any content
+  // that comes before this META tag, such as a TITLE tag.
+  $elements['system_meta_content_type'] = array(
+    '#type' => 'html_tag',
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'http-equiv' => 'Content-Type',
+      'content' => 'text/html; charset=utf-8',
+    ),
+    // Security: This always has to be output first.
+    '#weight' => -1000,
+  );
+  // Show Drupal and the major version number in the META GENERATOR tag.
+  // Get the major version.
+  list($version, ) = explode('.', VERSION);
+  $elements['system_meta_generator'] = array(
+    '#type' => 'html_tag',
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'name' => 'Generator',
+      'content' => 'Drupal ' . $version . ' (http://drupal.org)',
+    ),
+  );
+  // Also send the generator in the HTTP header.
+  $elements['system_meta_generator']['#attached']['drupal_set_header'][] = array('X-Generator', $elements['system_meta_generator']['#attributes']['content']);
+  return $elements;
+}
+
+/**
+ * Retrieve output to be displayed in the HEAD tag of the HTML page.
  */
 function drupal_get_html_head() {
-  $output = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
-  return $output . drupal_add_html_head();
+  $elements = drupal_add_html_head();
+  drupal_alter('html_head', $elements);
+  return drupal_render($elements);
 }
 
 /**
@@ -319,10 +374,10 @@ function drupal_clear_path_cache() {
 function drupal_add_feed($url = NULL, $title = '') {
   $stored_feed_links = &drupal_static(__FUNCTION__, array());
 
-  if (!is_null($url) && !isset($stored_feed_links[$url])) {
+  if (isset($url)) {
     $stored_feed_links[$url] = theme('feed_icon', array('url' => $url, 'title' => $title));
 
-    drupal_add_link(array('rel' => 'alternate',
+    drupal_add_html_head_link(array('rel' => 'alternate',
                           'type' => 'application/rss+xml',
                           'title' => $title,
                           'href' => $url));
@@ -2469,6 +2524,28 @@ function url($path = NULL, array $option
 }
 
 /**
+ * Format an attribute string for a HTTP header.
+ *
+ * @param $attributes
+ *   An associative array of attributes such as 'rel'.
+ *
+ * @return
+ *   A ; separated string ready for insertion in a HTTP header. No escaping is
+ *   performed for HTML entities, so this string is not safe to be printed.
+ *
+ * @see drupal_set_header()
+ */
+function drupal_header_attributes(array $attributes = array()) {
+  foreach ($attributes as $attribute => &$data) {
+    if (is_array($data)) {
+      $data = implode(' ', $data);
+    }
+    $data = $attribute . '="' . $data . '"';
+  }
+  return $attributes ? ' ' . implode('; ', $attributes) : '';
+}
+
+/**
  * Format an attribute string to insert in a tag.
  *
  * Each array key and its value will be formatted into an HTML attribute string.
@@ -2878,12 +2955,32 @@ function base_path() {
 }
 
 /**
- * Add a <link> tag to the page's HEAD.
+ * Add a LINK tag with a distinct 'rel' attribute to the page's HEAD.
  *
- * This function can be called as long the HTML header hasn't been sent.
- */
-function drupal_add_link($attributes) {
-  drupal_add_html_head('<link' . drupal_attributes($attributes) . " />\n");
+ * This function can be called as long the HTML header hasn't been sent,
+ * which on normal pages is up through the preprocess step of theme('html').
+ * Adding a link will overwrite a prior link with the exact same 'rel' and
+ * 'href' attributes.
+ * 
+ * @param $attributes
+ *   Associative array of element attributes including 'href' and 'rel'.
+ * @param $header
+ *   Optional flag to determine if a HTTP 'Link:' header should be sent.
+ */
+function drupal_add_html_head_link($attributes, $header = FALSE) {
+  $element = array(
+    '#tag' => 'link',
+    '#attributes' => $attributes,
+  );
+
+  drupal_add_html_head($element, 'drupal_add_html_head_link:' . $attributes['rel'] . ':' . $attributes['href']);
+
+  if ($header) {
+    // Also add a HTTP header "Link:".
+    $href = '<' . check_plain($attributes['href']) . '>;';
+    unset($attributes['href']);
+    $element['#attached']['drupal_set_header'][] = array('Link',  $href . drupal_header_attributes($attributes), TRUE);
+  }
 }
 
 /**
@@ -3069,7 +3166,15 @@ function drupal_get_css($css = NULL) {
   }
 
   // If CSS preprocessing is off, we still need to output the styles.
-  // Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones.
+  // Additionally, go through any remaining styles if CSS preprocessing is on
+  // and output the non-cached ones.
+  $css_element = array(
+    '#tag' => 'link',
+    '#attributes' => array(
+      'type' => 'text/css',
+      'rel' => 'stylesheet',
+    ),
+  );
   $rendered_css = array();
   $inline_css = '';
   $external_css = '';
@@ -3081,7 +3186,10 @@ function drupal_get_css($css = NULL) {
       case 'file':
         // Depending on whether aggregation is desired, include the file.
         if (!$item['preprocess'] || !($is_writable && $preprocess_css)) {
-          $rendered_css[] = '<link type="text/css" rel="stylesheet" media="' . $item['media'] . '" href="' . file_create_url($item['data']) . $query_string . '" />';
+          $element = $css_element;
+          $element['#attributes']['media'] = $item['media'];
+          $element['#attributes']['href'] = file_create_url($item['data']) . $query_string;
+          $rendered_css[] = theme('html_tag', array('element' => $element));
         }
         else {
           $preprocess_items[$item['media']][] = $item;
@@ -3096,7 +3204,10 @@ function drupal_get_css($css = NULL) {
         break;
       case 'external':
         // Preprocessing for external CSS files is ignored.
-        $external_css .= '<link type="text/css" rel="stylesheet" media="' . $item['media'] . '" href="' . $item['data'] . '" />' . "\n";
+        $element = $css_element;
+        $element['#attributes']['media'] = $item['media'];
+        $element['#attributes']['href'] = $item['data'];
+        $external_css .= theme('html_tag', array('element' => $element));
         break;
     }
   }
@@ -3105,18 +3216,24 @@ function drupal_get_css($css = NULL) {
     foreach ($preprocess_items as $media => $items) {
       // Prefix filename to prevent blocking by firewalls which reject files
       // starting with "ad*".
+      $element = $css_element;
+      $element['#attributes']['media'] = $media;
       $filename = 'css_' . md5(serialize($items) . $query_string) . '.css';
-      $preprocess_file = file_create_url(drupal_build_css_cache($items, $filename));
-      $rendered_css['preprocess'] .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . $preprocess_file . '" />' . "\n";
+      $element['#attributes']['href'] = file_create_url(drupal_build_css_cache($items, $filename));
+      $rendered_css['preprocess'] .= theme('html_tag', array('element' => $element));
     }
   }
   // Enclose the inline CSS with the style tag if required.
   if (!empty($inline_css)) {
-    $inline_css = "\n" . '<style type="text/css">' . $inline_css .'</style>';
+    $element = $css_element;
+    $element['#tag'] = 'style';
+    $element['#value'] = $inline_css;
+    unset($element['#attributes']['rel']);
+    $inline_css = "\n" . theme('html_tag', array('element' => $element));
   }
 
   // Output all the CSS files with the inline stylesheets showing up last.
-  return implode("\n", $rendered_css) . $external_css . $inline_css;
+  return implode($rendered_css) . $external_css . $inline_css;
 }
 
 /**
@@ -3597,7 +3714,7 @@ function drupal_get_js($scope = 'header'
 
   $output = '';
   $preprocessed = '';
-  $no_preprocess = '';
+  $no_preprocess = "\n";
   $files = array();
   $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
   $directory = file_directory_path('public');
@@ -3621,19 +3738,42 @@ function drupal_get_js($scope = 'header'
   uasort($items, 'drupal_sort_weight');
 
   // Loop through the JavaScript to construct the rendered output.
+  $element = array(
+    '#tag' => 'script',
+    '#value' => '',
+    '#attributes' => array(
+      'type' => 'text/javascript',
+    ),
+  );
   foreach ($items as $item) {
     switch ($item['type']) {
       case 'setting':
-        $output .= '<script type="text/javascript">' . $embed_prefix . 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(call_user_func_array('array_merge_recursive', $item['data'])) . ");" . $embed_suffix . "</script>\n";
+        $js_element = $element;
+        $js_element['#value_prefix'] = $embed_prefix;
+        $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(call_user_func_array('array_merge_recursive', $item['data'])) . ");";
+        $js_element['#value_suffix'] = $embed_suffix;
+        $output .= theme('html_tag', array('element' => $js_element));
         break;
 
       case 'inline':
-        $output .= '<script type="text/javascript"' . ($item['defer'] ? ' defer="defer"' : '') . '>' . $embed_prefix . $item['data'] . $embed_suffix . "</script>\n";
+        $js_element = $element;
+        if ($item['defer']) {
+          $js_element['#attributes']['defer'] = 'defer';
+        }
+        $js_element['#value_prefix'] = $embed_prefix;
+        $js_element['#value'] = $item['data'];
+        $js_element['#value_suffix'] = $embed_suffix;
+        $output .= theme('html_tag', array('element' => $js_element));
         break;
 
       case 'file':
+        $js_element = $element;
         if (!$item['preprocess'] || !$is_writable || !$preprocess_js) {
-          $no_preprocess .= '<script type="text/javascript"' . ($item['defer'] ? ' defer="defer"' : '') . ' src="' . file_create_url($item['data']) . ($item['cache'] ? $query_string : '?' . REQUEST_TIME) . "\"></script>\n";
+          if ($item['defer']) {
+            $js_element['#attributes']['defer'] = 'defer';
+          }
+          $js_element['#attributes']['src'] = file_create_url($item['data']) . ($item['cache'] ? $query_string : '?' . REQUEST_TIME);
+          $no_preprocess .= theme('html_tag', array('element' => $js_element));
         }
         else {
           $files[$item['data']] = $item;
@@ -3641,8 +3781,13 @@ function drupal_get_js($scope = 'header'
         break;
 
       case 'external':
+        $js_element = $element;
         // Preprocessing for external JavaScript files is ignored.
-        $output .= '<script type="text/javascript"' . ($item['defer'] ? ' defer="defer"' : '') . ' src="' . check_plain($item['data']) . "\"></script>\n";
+        if ($item['defer']) {
+          $js_element['#attributes']['defer'] = 'defer';
+        }
+        $js_element['#attributes']['src'] = $item['data'];
+        $output .= theme('html_tag', array('element' => $js_element));
         break;
     }
   }
@@ -3653,7 +3798,9 @@ function drupal_get_js($scope = 'header'
     // starting with "ad*".
     $filename = 'js_' . md5(serialize($files) . $query_string) . '.js';
     $preprocess_file = file_create_url(drupal_build_js_cache($files, $filename));
-    $preprocessed .= '<script type="text/javascript" src="' . $preprocess_file . '"></script>' . "\n";
+    $js_element = $element;
+    $js_element['#attributes']['src'] = $preprocess_file;
+    $preprocessed .= theme('html_tag', array('element' => $js_element)) . "\n";
   }
 
   // Keep the order of JS files consistent as some are preprocessed and others are not.
@@ -5051,6 +5198,9 @@ function drupal_common_theme() {
     'indentation' => array(
       'arguments' => array('size' => 1),
     ),
+    'html_tag' => array(
+      'arguments' => array('element' => array()),
+    ),
     // from theme.maintenance.inc
     'maintenance_page' => array(
       'arguments' => array('content' => NULL, 'show_messages' => TRUE),
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.536
diff -u -p -r1.536 theme.inc
--- includes/theme.inc	15 Oct 2009 12:27:34 -0000	1.536
+++ includes/theme.inc	16 Oct 2009 00:12:17 -0000
@@ -1879,6 +1879,48 @@ function theme_feed_icon($variables) {
 }
 
 /**
+ * Generate the output for a generic HTML tag with attributes.
+ *
+ * This theme function should be used for tags appearing in the HTML HEAD of a
+ * document (specified via the #tag property in the passed $element):
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array describing the tag:
+ *     - #tag: The tag name to output.  Typical tags added to the HTML HEAD:
+ *       - meta: To provide meta information, such as a page refresh.
+ *       - link: To refer to stylesheets and other contextual information.
+ *       - script: To load JavaScript.
+ *     - #attributes: (optional) An array of HTML attributes to apply to the
+ *       tag.
+ *     - #value: (optional) A string containing tag content, such as inline CSS.
+ *     - #value_prefix: (optional) A string to prepend to #value, e.g. a CDATA
+ *       wrapper prefix.
+ *     - #value_suffix: (optional) A string to append to #value, e.g. a CDATA
+ *       wrapper suffix.
+ *
+ * @ingroup themeable
+ */
+function theme_html_tag($variables) {
+  $element = $variables['element'];
+  if (!isset($element['#value'])) {
+    return '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . " />\n";
+  }
+  else {
+    $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>';
+    if (isset($element['#value_prefix'])) {
+      $output .= $element['#value_prefix'];
+    }
+    $output .= $element['#value'];
+    if (isset($element['#value_suffix'])) {
+      $output .= $element['#value_suffix'];
+    }
+    $output .= '</' . $element['#tag'] . ">\n";
+    return $output;
+  }
+}
+
+/**
  * Returns code that emits the 'more' link used on blocks.
  *
  * @param $variables
@@ -2175,7 +2217,7 @@ function template_preprocess_html(&$vari
   if (theme_get_setting('toggle_favicon')) {
     $favicon = theme_get_setting('favicon');
     $type = theme_get_setting('favicon_mimetype');
-    drupal_add_html_head('<link rel="shortcut icon" href="' . check_url($favicon) . '" type="' . check_plain($type) . '" />');
+    drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => check_url($favicon), 'type' => $type));
   }
 
   // Construct page title.
@@ -2344,7 +2386,7 @@ function template_preprocess_maintenance
   if (theme_get_setting('toggle_favicon')) {
     $favicon = theme_get_setting('favicon');
     $type = theme_get_setting('favicon_mimetype');
-    drupal_add_html_head('<link rel="shortcut icon" href="' . check_url($favicon) . '" type="' . check_plain($type) . '" />');
+    drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => check_url($favicon), 'type' => $type));
   }
 
   global $theme;
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.519
diff -u -p -r1.519 book.module
--- modules/book/book.module	15 Oct 2009 14:07:26 -0000	1.519
+++ modules/book/book.module	16 Oct 2009 00:12:23 -0000
@@ -940,21 +940,21 @@ function template_preprocess_book_naviga
 
     if ($prev = book_prev($book_link)) {
       $prev_href = url($prev['href']);
-      drupal_add_link(array('rel' => 'prev', 'href' => $prev_href));
+      drupal_add_html_head_link(array('rel' => 'prev', 'href' => $prev_href));
       $variables['prev_url'] = $prev_href;
       $variables['prev_title'] = check_plain($prev['title']);
     }
 
     if ($book_link['plid'] && $parent = book_link_load($book_link['plid'])) {
       $parent_href = url($parent['href']);
-      drupal_add_link(array('rel' => 'up', 'href' => $parent_href));
+      drupal_add_html_head_link(array('rel' => 'up', 'href' => $parent_href));
       $variables['parent_url'] = $parent_href;
       $variables['parent_title'] = check_plain($parent['title']);
     }
 
     if ($next = book_next($book_link)) {
       $next_href = url($next['href']);
-      drupal_add_link(array('rel' => 'next', 'href' => $next_href));
+      drupal_add_html_head_link(array('rel' => 'next', 'href' => $next_href));
       $variables['next_url'] = $next_href;
       $variables['next_title'] = check_plain($next['title']);
     }
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.787
diff -u -p -r1.787 comment.module
--- modules/comment/comment.module	15 Oct 2009 16:18:45 -0000	1.787
+++ modules/comment/comment.module	16 Oct 2009 00:12:34 -0000
@@ -360,9 +360,6 @@ function comment_permalink($comment) {
     $_GET['q'] = 'node/' . $node->nid;
     $_GET['page'] = $page;
 
-    // Set the node path as the canonical URL to prevent duplicate content.
-    drupal_add_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)));
-
     // Return the node view, this will show the correct comment in context.
     return menu_execute_active_handler('node/' . $node->nid, FALSE);
   }
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1146
diff -u -p -r1.1146 node.module
--- modules/node/node.module	15 Oct 2009 12:44:36 -0000	1.1146
+++ modules/node/node.module	16 Oct 2009 00:12:40 -0000
@@ -2104,6 +2104,10 @@ function node_page_view($node) {
   if (isset($return['nodes'][$node->nid]['title'])) {
     drupal_set_title($return['nodes'][$node->nid]['title']['items'][0]['#item']['value']);
   }
+  // Set the node path as the canonical URL to prevent duplicate content.
+  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)), TRUE);
+  // Set the non-aliased path as a default shortlink.
+  drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url('node/' . $node->nid, array('alias' => TRUE))), TRUE);
   return $return;
 }
 
Index: modules/openid/tests/openid_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/tests/openid_test.module,v
retrieving revision 1.5
diff -u -p -r1.5 openid_test.module
--- modules/openid/tests/openid_test.module	30 Sep 2009 18:36:02 -0000	1.5
+++ modules/openid/tests/openid_test.module	16 Oct 2009 00:12:43 -0000
@@ -97,7 +97,14 @@ function openid_test_yadis_x_xrds_locati
  * Menu callback; regular HTML page with <meta> element.
  */
 function openid_test_yadis_http_equiv() {
-  drupal_add_html_head('<meta http-equiv="X-XRDS-Location" content="' . url('openid-test/yadis/xrds', array('absolute' => TRUE)) . '" />');
+  $element = array(
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'http-equiv' => 'X-XRDS-Location',
+      'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)),
+    ),
+  );
+  drupal_add_html_head($element, 'openid_test_yadis_http_equiv');
   return t('This page includes a &lt;meta equiv=...&gt; element containing the URL of an XRDS document.');
 }
 
@@ -105,7 +112,7 @@ function openid_test_yadis_http_equiv() 
  * Menu callback; regular HTML page with OpenID 1.0 <link> element.
  */
 function openid_test_html_openid1() {
-  drupal_add_html_head('<link rel="openid.server" href="' . url('openid-test/endpoint', array('absolute' => TRUE)) . '" />');
+  drupal_add_html_head_link(array('rel' => 'openid.server', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
   return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
 }
 
@@ -113,7 +120,7 @@ function openid_test_html_openid1() {
  * Menu callback; regular HTML page with OpenID 2.0 <link> element.
  */
 function openid_test_html_openid2() {
-  drupal_add_html_head('<link rel="openid2.provider" href="' . url('openid-test/endpoint', array('absolute' => TRUE)) . '" />');
+  drupal_add_html_head_link(array('rel' => 'openid2.provider', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
   return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
 }
 
Index: modules/simpletest/tests/browser_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/browser_test.module,v
retrieving revision 1.4
diff -u -p -r1.4 browser_test.module
--- modules/simpletest/tests/browser_test.module	30 Sep 2009 18:36:02 -0000	1.4
+++ modules/simpletest/tests/browser_test.module	16 Oct 2009 00:12:43 -0000
@@ -57,7 +57,14 @@ function browser_test_print_post_form_su
 function browser_test_refresh_meta() {
   if (!isset($_GET['refresh'])) {
     $url = url('browser_test/refresh/meta', array('absolute' => TRUE, 'query' => array('refresh' => 'true')));
-    drupal_add_html_head('<meta http-equiv="Refresh" content="0; URL=' . $url . '">');
+    $element = array(
+      '#tag' => 'meta',
+      '#attributes' => array(
+        'http-equiv' => 'Refresh',
+        'content' => '0; URL=' . $url,
+      ),
+    );
+    drupal_add_html_head($element, 'browser_test_refresh_meta');
     return '';
   }
   echo 'Refresh successful';
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.84
diff -u -p -r1.84 common.test
--- modules/simpletest/tests/common.test	13 Oct 2009 16:38:43 -0000	1.84
+++ modules/simpletest/tests/common.test	16 Oct 2009 00:12:46 -0000
@@ -530,7 +530,7 @@ class CascadingStylesheetsTestCase exten
     $css_preprocessed = '<style type="text/css">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
     drupal_add_css($css, 'inline');
     $styles = drupal_get_css();
-    $this->assertEqual($styles, "\n" . $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
+    $this->assertEqual($styles, "\n" . $css_preprocessed . "\n", t('Rendering preprocessed inline CSS adds it to the page.'));
   }
 
   /**
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.90
diff -u -p -r1.90 system.api.php
--- modules/system/system.api.php	15 Oct 2009 17:55:55 -0000	1.90
+++ modules/system/system.api.php	16 Oct 2009 00:12:56 -0000
@@ -2276,6 +2276,27 @@ function hook_drupal_goto_alter(&$path, 
 }
 
 /**
+ * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head().
+ *
+ * Elements available to be altered are only those added using
+ * drupal_add_html_head_link() or drupal_add_html_head(). CSS and JS files
+ * are handled using drupal_add_css() and drupal_add_js(), so the head links
+ * for those files will not appear in the $head_elements array.
+ *
+ * @param $head_elements
+ *   An array of renderable elements. Generally the values of the #attributes
+ *   array will be the most likely target for changes.
+ */
+function hook_html_head_alter(&$head_elements) {
+  foreach($head_elements as $key => $element) {
+    if (isset($element['#attributes']['rel']) && $element['#attributes']['rel'] == 'canonical') {
+      // I want a custom canonical url.
+      $head_elements[$key]['#attributes']['href'] = mymodule_canonical_url();
+    }
+  }
+}
+
+/**
  * Alter the full list of installation tasks.
  *
  * @param $tasks
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.816
diff -u -p -r1.816 system.module
--- modules/system/system.module	15 Oct 2009 21:19:31 -0000	1.816
+++ modules/system/system.module	16 Oct 2009 00:13:05 -0000
@@ -201,12 +201,6 @@ function system_theme() {
     'system_powered_by' => array(
       'arguments' => array('image_path' => NULL),
     ),
-    'meta_generator_html' => array(
-      'arguments' => array('version' => NULL),
-    ),
-    'meta_generator_header' => array(
-      'arguments' => array('version' => NULL),
-    ),
     'system_compact_link' => array(),
     'system_run_cron_image' => array(
       'arguments' => array('image_path' => NULL),
@@ -322,6 +316,12 @@ function system_element_info() {
     '#ajax_commands' => array(),
   );
 
+  $types['html_tag'] = array(
+    '#theme' => 'html_tag',
+    '#attributes' => array(),
+    '#value' => NULL,
+  );
+
   // Input elements.
   $types['submit'] = array(
     '#input' => TRUE,
@@ -2911,24 +2911,6 @@ function theme_system_compact_link() {
 }
 
 /**
- * Send Drupal and the major version number in the META GENERATOR HTML.
- *
- * @ingroup themeable
- */
-function theme_meta_generator_html($variables) {
-  drupal_add_html_head('<meta name="Generator" content="Drupal ' . $variables['version'] . ' (http://drupal.org)" />');
-}
-
-/**
- * Send Drupal and the major version number in the HTTP headers.
- *
- * @ingroup themeable
- */
-function theme_meta_generator_header($variables) {
-  drupal_add_http_header('X-Generator', 'Drupal ' . $variables['version'] . ' (http://drupal.org)');
-}
-
-/**
  * Implement hook_image_toolkits().
  */
 function system_image_toolkits() {
