Index: modules/field/modules/text/text.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.test,v
retrieving revision 1.21
diff -u -p -r1.21 text.test
--- modules/field/modules/text/text.test	27 Mar 2010 05:52:49 -0000	1.21
+++ modules/field/modules/text/text.test	9 Apr 2010 19:22:25 -0000
@@ -309,11 +309,11 @@ class TextSummaryTestCase extends Drupal
     $expected_lb = array(
       "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
       "",
-      "<p />",
-      "<p />",
-      "<p />",
-      "<p />",
-      "<p />",
+      "<p></p>",
+      "<p></p>",
+      "<p></p>",
+      "<p></p>",
+      "<p></p>",
       "<p>\nHi</p>",
       "<p>\nHi</p>",
       "<p>\nHi</p>",
Index: modules/filter/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v
retrieving revision 1.324
diff -u -p -r1.324 filter.module
--- modules/filter/filter.module	26 Mar 2010 17:14:45 -0000	1.324
+++ modules/filter/filter.module	9 Apr 2010 19:22:25 -0000
@@ -1018,7 +1018,64 @@ function filter_dom_serialize($dom_docum
   foreach ($body_node->childNodes as $child_node) {
     $body_content .= $dom_document->saveXML($child_node);
   }
-  return preg_replace('|<([^>]*)/>|i', '<$1 />', $body_content);
+
+  return preg_replace_callback('|<(\w+)([^>]*)\s*/>|i', '_filter_empty_element_ensure_html_compatibility', $body_content);
+}
+
+/**
+ * Ensures that empty elements in an XHTML document are compatible with HTML.
+ *
+ * In order to ensure compatibility with HTML user agents, we require empty
+ * elements whose content model is EMPTY to have a space before the trailing
+ * slash (e.g., <br /> rather than <br/>) and empty elements whose content
+ * model is not EMPTY to have separate opening and closing tags (e.g.,
+ * <div></div> rather than <div />).
+ *
+ * For a reference on ensuring XHTML compatibility with HTML user agents, see
+ * the relevant section in the @link http://www.w3.org/TR/xhtml1/#guidelines
+ * W3C XHTML 1.0 recommendation @endlink, as well as the @link
+ * http://www.php.net/manual/en/domdocument.savexml.php#95252 comments on the
+ * PHP DOMDocument::saveXML function @endlink which Drupal uses to prepare the
+ * input for this function.
+ *
+ * @param $matches
+ *   An array of matches, passed in from preg_replace_callback(). These should
+ *   correspond to an empty HTML tag that has been closed, perhaps incorrectly.
+ *   The first match should contain the name of the HTML tag, and the second
+ *   match should contain any properties that come after the tag (e.g., if the
+ *   tag is <div class="test" />, then 'div' and ' class="test"' are the
+ *   matches passed in to this function).
+ *
+ * @return
+ *   Proper HTML for the opening and closing of the passed-in HTML tag (e.g.,
+ *   <div class="test"></div> in the above example).
+ *
+ * @see filter_dom_serialize()
+ */
+function _filter_empty_element_ensure_html_compatibility($matches) {
+  // Define an array of elements whose content model is expected to be EMPTY
+  // (see the W3C DTDs at http://www.w3.org/TR/xhtml1/dtds.html for the source
+  // of this list).
+  $allowed_empty_elements = array(
+    'area',
+    'base',
+    'basefont',
+    'br',
+    'col',
+    'frame',
+    'hr',
+    'img',
+    'input',
+    'isindex',
+    'link',
+    'meta',
+    'param',
+  );
+  // Properly output HTML for an empty element depending on whether the element
+  // appears in the above list.
+  $element = $matches[1];
+  $element_content = $matches[1] . $matches[2];
+  return in_array($element, $allowed_empty_elements) ? "<$element_content />" : "<$element_content></$element>";
 }
 
 /**
Index: modules/filter/filter.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v
retrieving revision 1.63
diff -u -p -r1.63 filter.test
--- modules/filter/filter.test	31 Mar 2010 20:05:06 -0000	1.63
+++ modules/filter/filter.test	9 Apr 2010 19:22:25 -0000
@@ -1003,7 +1003,7 @@ class FilterUnitTestCase extends DrupalU
     $this->assertEqual($f, '<p>text</p>', t('HTML corrector -- tag closing at the end of input.'));
 
     $f = _filter_htmlcorrector('<p>text<p><p>text');
-    $this->assertEqual($f, '<p>text</p><p /><p>text</p>', t('HTML corrector -- tag closing.'));
+    $this->assertEqual($f, '<p>text</p><p></p><p>text</p>', t('HTML corrector -- tag closing.'));
 
     $f = _filter_htmlcorrector("<ul><li>e1<li>e2");
     $this->assertEqual($f, "<ul><li>e1</li><li>e2</li></ul>", t('HTML corrector -- unclosed list tags.'));
@@ -1021,14 +1021,17 @@ class FilterUnitTestCase extends DrupalU
     $f = _filter_htmlcorrector('<P>test</p>');
     $this->assertEqual($f, '<p>test</p>', t('HTML corrector -- Convert uppercased tags to proper lowercased ones.'));
 
+    $f = _filter_htmlcorrector('test<hr />');
+    $this->assertEqual($f, 'test<hr />', t('HTML corrector -- Let proper XHTML pass through.'));
+
     $f = _filter_htmlcorrector('test<hr/>');
-    $this->assertEqual($f, 'test<hr />', t('HTML corrector -- Let proper XHTML pass thru.'));
+    $this->assertEqual($f, 'test<hr />', t('HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.'));
 
-    $f = _filter_htmlcorrector('test<hr />');
-    $this->assertEqual($f, 'test<hr />', t('HTML corrector -- Let proper XHTML pass thru.'));
+    $f = _filter_htmlcorrector('test<hr    />');
+    $this->assertEqual($f, 'test<hr />', t('HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.'));
 
     $f = _filter_htmlcorrector('<span class="test" />');
-    $this->assertEqual($f, '<span class="test" />', t('HTML corrector -- Let proper XHTML pass thru.'));
+    $this->assertEqual($f, '<span class="test"></span>', t('HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.'));
 
     $f = _filter_htmlcorrector('test1<br class="test">test2');
     $this->assertEqual($f, 'test1<br class="test" />test2', t('HTML corrector -- Automatically close single tags.'));
@@ -1042,6 +1045,12 @@ class FilterUnitTestCase extends DrupalU
     $f = _filter_htmlcorrector('<img src="http://example.com/test.jpg">test</img>');
     $this->assertEqual($f, '<img src="http://example.com/test.jpg" />test', t('HTML corrector -- Automatically close single tags.'));
 
+    $f = _filter_htmlcorrector('<br></br>');
+    $this->assertEqual($f, '<br />', t("HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY."));
+
+    $f = _filter_htmlcorrector('<div></div>');
+    $this->assertEqual($f, '<div></div>', t("HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY."));
+
     $f = _filter_htmlcorrector('<p>line1<br/><hr/>line2</p>');
     $this->assertEqual($f, '<p>line1<br /></p><hr />line2', t('HTML corrector -- Move non-inline elements outside of inline containers.'));
 
